摘要:前端基本功常見概念一點這里前端基本功常見概念二點這里前端基本功常見概念三點這里超文本標記語言,顯示信息,不區分大小寫升級版的,區分大小寫可擴展標記語言被用來傳輸和存儲數據規范采用異步方式加載模塊,模塊的加載不影響它后面語句的運行。
前端基本功-常見概念(一) 點這里
前端基本功-常見概念(二) 點這里
前端基本功-常見概念(三) 點這里
html:超文本標記語言,顯示信息,不區分大小寫
xhtml:升級版的html,區分大小寫
xml:可擴展標記語言被用來傳輸和存儲數據
2.AMD/CMD/CommonJs/ES6 Module
AMD:AMD規范采用異步方式加載模塊,模塊的加載不影響它后面語句的運行。所有依賴這個模塊的語句,都定義在一個回調函數中,等到加載完成之后,這個回調函數才會運行。
AMD是requirejs 在推廣過程中對模塊定義的規范化產出,提前執行,推崇依賴前置。用define()定義模塊,用require()加載模塊,require.config()指定引用路徑等
首先我們需要引入require.js文件和一個入口文件main.js。main.js中配置require.config()并規定項目中用到的基礎模塊。
/** 網頁中引入require.js及main.js **/ /** main.js 入口文件/主模塊 **/ // 首先用config()指定各模塊路徑和引用名 require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", //實際路徑為js/lib/jquery.min.js "underscore": "underscore.min", } }); // 執行基本操作 require(["jquery","underscore"],function($,_){ // some code here });
引用模塊的時候,我們將模塊名放在[]中作為reqiure()的第一參數;如果我們定義的模塊本身也依賴其他模塊,那就需要將它們放在[]中作為define()的第一參數。
// 定義math.js模塊 define(function () { var basicNum = 0; var add = function (x, y) { return x + y; }; return { add: add, basicNum :basicNum }; }); // 定義一個依賴underscore.js的模塊 define(["underscore"],function(_){ var classify = function(list){ _.countBy(list,function(num){ return num > 30 ? "old" : "young"; }) }; return { classify :classify }; }) // 引用模塊,將模塊放在[]內 require(["jquery", "math"],function($, math){ var sum = math.add(10,20); $("#sum").html(sum); });
CMD:seajs 在推廣過程中對模塊定義的規范化產出,延遲執行,推崇依賴就近
require.js在申明依賴的模塊時會在第一之間加載并執行模塊內的代碼:
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { // 等于在最前面聲明并初始化了要用到的所有模塊 if (false) { // 即便沒用到某個模塊 b,但 b 還是提前執行了 b.foo() } });
CMD是另一種js模塊化方案,它與AMD很類似,不同點在于:AMD 推崇依賴前置、提前執行,CMD推崇依賴就近、延遲執行。此規范其實是在sea.js推廣過程中產生的。
/** CMD寫法 **/ define(function(require, exports, module) { var a = require("./a"); //在需要時申明 a.doSomething(); if (false) { var b = require("./b"); b.doSomething(); } }); /** sea.js **/ // 定義模塊 math.js define(function(require, exports, module) { var $ = require("jquery.js"); var add = function(a,b){ return a+b; } exports.add = add; }); // 加載模塊 seajs.use(["math.js"], function(math){ var sum = math.add(1+2); });
CommonJs:Node.js是commonJS規范的主要實踐者,它有四個重要的環境變量為模塊化的實現提供支持:module、exports、require、global。實際使用時,用module.exports定義當前模塊對外輸出的接口(不推薦直接用exports),用require加載模塊。
// 定義模塊math.js var basicNum = 0; function add(a, b) { return a + b; } module.exports = { //在這里寫上需要向外暴露的函數、變量 add: add, basicNum: basicNum } // 引用自定義的模塊時,參數包含路徑,可省略.js var math = require("./math"); math.add(2, 5); // 引用核心模塊時,不需要帶路徑 var http = require("http"); http.createService(...).listen(3000);
commonJS用同步的方式加載模塊。在服務端,模塊文件都存在本地磁盤,讀取非常快,所以這樣做不會有問題。但是在瀏覽器端,限于網絡原因,更合理的方案是使用異步加載。
ES6 Module:ES6 在語言標準的層面上,實現了模塊功能,而且實現得相當簡單,旨在成為瀏覽器和服務器通用的模塊解決方案。其模塊功能主要由兩個命令構成:export和import。export命令用于規定模塊的對外接口,import命令用于輸入其他模塊提供的功能。
/** 定義模塊 math.js **/ var basicNum = 0; var add = function (a, b) { return a + b; }; export { basicNum, add }; /** 引用模塊 **/ import { basicNum, add } from "./math"; function test(ele) { ele.textContent = add(99 + basicNum); }
如上例所示,使用import命令的時候,用戶需要知道所要加載的變量名或函數名。其實ES6還提供了export default命令,為模塊指定默認輸出,對應的import語句不需要使用大括號。這也更趨近于ADM的引用寫法。
/** export default **/ //定義輸出 export default { basicNum, add }; //引入 import math from "./math"; function test(ele) { ele.textContent = math.add(99 + math.basicNum); }
ES6的模塊不是對象,import命令會被 JavaScript 引擎靜態分析,在編譯時就引入模塊代碼,而不是在代碼運行時加載,所以無法實現條件加載。也正因為這個,使得靜態分析成為可能。
ES6 模塊與 CommonJS 模塊的差異
CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。
CommonJS 模塊輸出的是值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。
ES6 模塊的運行機制與 CommonJS 不一樣。JS 引擎對腳本靜態分析的時候,遇到模塊加載命令import,就會生成一個只讀引用。等到腳本真正執行時,再根據這個只讀引用,到被加載的那個模塊里面去取值。換句話說,ES6 的import有點像 Unix 系統的“符號連接”,原始值變了,import加載的值也會跟著變。因此,ES6 模塊是動態引用,并且不會緩存值,模塊里面的變量綁定其所在的模塊。
CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。
運行時加載: CommonJS 模塊就是對象;即在輸入時是先加載整個模塊,生成一個對象,然后再從這個對象上面讀取方法,這種加載稱為“運行時加載”。
- 編譯時加載: ES6 模塊不是對象,而是通過 export 命令顯式指定輸出的代碼,import時采用靜態命令的形式。即在import時可以指定加載某個輸出值,而不是加載整個模塊,這種加載稱為“編譯時加載”。 CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完才會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。
本節參考文章:前端模塊化:CommonJS,AMD,CMD,ES6
3.ES5的繼承/ES6的繼承ES5的繼承時通過prototype或構造函數機制來實現。ES5的繼承實質上是先創建子類的實例對象,然后再將父類的方法添加到this上(Parent.apply(this))。
ES6的繼承機制完全不同,實質上是先創建父類的實例對象this(所以必須先調用父類的super()方法),然后再用子類的構造函數修改this。
具體的:ES6通過class關鍵字定義類,里面有構造方法,類之間通過extends關鍵字實現繼承。子類必須在constructor方法中調用super方法,否則新建實例報錯。因為子類沒有自己的this對象,而是繼承了父類的this對象,然后對其進行加工。如果不調用super方法,子類得不到this對象。
ps:super關鍵字指代父類的實例,即父類的this對象。在子類構造函數中,調用super后,才可使用this關鍵字,否則報錯。
區別:(以SubClass,SuperClass,instance為例)
ES5中繼承的實質是:(那種經典寄生組合式繼承法)通過prototype或構造函數機制來實現,先創建子類的實例對象,然后再將父類的方法添加到this上(Parent.apply(this))。
先由子類(SubClass)構造出實例對象this
然后在子類的構造函數中,將父類(SuperClass)的屬性添加到this上,SuperClass.apply(this, arguments)
子類原型(SubClass.prototype)指向父類原型(SuperClass.prototype)
所以instance是子類(SubClass)構造出的(所以沒有父類的[[Class]]關鍵標志)
所以,instance有SubClass和SuperClass的所有實例屬性,以及可以通過原型鏈回溯,獲取SubClass和SuperClass原型上的方法
ES6中繼承的實質是:先創建父類的實例對象this(所以必須先調用父類的super()方法),然后再用子類的構造函數修改this
先由父類(SuperClass)構造出實例對象this,這也是為什么必須先調用父類的super()方法(子類沒有自己的this對象,需先由父類構造)
然后在子類的構造函數中,修改this(進行加工),譬如讓它指向子類原型(SubClass.prototype),這一步很關鍵,否則無法找到子類原型(注,子類構造中加工這一步的實際做法是推測出的,從最終效果來推測)
然后同樣,子類原型(SubClass.prototype)指向父類原型(SuperClass.prototype)
所以instance是父類(SuperClass)構造出的(所以有著父類的[[Class]]關鍵標志)
所以,instance有SubClass和SuperClass的所有實例屬性,以及可以通過原型鏈回溯,獲取SubClass和SuperClass原型上的方法
靜態方法繼承實質上只需要更改下SubClass.__proto__到SuperClass即可
本節參考文章:鏈接
4.HTTP request報文/HTTP response報文請求報文 | 響應報文 |
---|---|
請求行 請求頭 空行 請求體 | 狀態行 響應頭 空行 響應體 |
HTTP request報文結構是怎樣的
首行是Request-Line包括:請求方法,請求URI,協議版本,CRLF
首行之后是若干行請求頭,包括general-header,request-header或者entity-header,每個一行以CRLF結束
請求頭和消息實體之間有一個CRLF分隔
根據實際請求需要可能包含一個消息實體 一個請求報文例子如下:
GET /Protocols/rfc2616/rfc2616-sec5.html HTTP/1.1 Host: www.w3.org Connection: keep-alive Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 Referer: https://www.google.com.hk/ Accept-Encoding: gzip,deflate,sdch Accept-Language: zh-CN,zh;q=0.8,en;q=0.6 Cookie: authorstyle=yes If-None-Match: "2cc8-3e3073913b100" If-Modified-Since: Wed, 01 Sep 2004 13:24:52 GMT name=qiu&age=25
請求報文
HTTP response報文結構是怎樣的
首行是狀態行包括:HTTP版本,狀態碼,狀態描述,后面跟一個CRLF
首行之后是若干行響應頭,包括:通用頭部,響應頭部,實體頭部
響應頭部和響應實體之間用一個CRLF空行分隔
最后是一個可能的消息實體 響應報文例子如下:
HTTP/1.1 200 OK Date: Tue, 08 Jul 2014 05:28:43 GMT Server: Apache/2 Last-Modified: Wed, 01 Sep 2004 13:24:52 GMT ETag: "40d7-3e3073913b100" Accept-Ranges: bytes Content-Length: 16599 Cache-Control: max-age=21600 Expires: Tue, 08 Jul 2014 11:28:43 GMT P3P: policyref="http://www.w3.org/2001/05/P3P/p3p.xml" Content-Type: text/html; charset=iso-8859-1 {"name": "qiu", "age": 25}
響應報文
5.面向對象的工廠模式/構造函數工廠模式集中實例化了對象,避免實例化對象大量重復問題
//工廠模式 function createObject(a,b){ var obj = new Object(); //集中實例化 obj.a = a; obj.b = b; obj.c = function () { return this.a + this.b; }; return obj; //返回實例化對象 } var box = createObject("abc",10); var box1 = createObject("abcdef",20); alert(box.c()); //返回abc10 alert(box1.c()); //返回abcdef20
//構造函數 function Create(a,b) { this.a =a; this.b =b; this.c = function () { return this.a + this.b; }; } var box = new Create("abc",10); alert(box.run()); //返回abc10
構造函數相比工廠模式:
沒有集中實例化
沒有返回對象實例
直接將屬性和方法賦值給this
解決了對象實例歸屬問題
構造函數編寫規范:
構造函數也是函數,但是函數名的第一個字母大寫
必須使用new運算符 + 函數名(首字母大寫)例如:var box = new Create();
構造函數和普通函數的區別:
普通函數,首字母無需大寫
構造函數,用普通函數調用方式無效
查看歸屬問題,要創建兩個構造函數:
function Create(a,b) { this.a =a; this.b =b; this.c = function () { return this.a + this.b; }; } function DeskTop(a,b) { this.a =a; this.b =b; this.c = function () { return this.a + this.b; }; } var box = new Create("abc",10); var box1 = new DeskTop("def",20); alert(box instanceof Object); //這里要注意:所有的構造函數的對象都是Object. alert(box instanceof Create); //true alert(box1 instanceof Create); //false alert(box1 instanceof DeskTop); //true6. new Promise / Promise.resolve()
Promise.resolve()可以生成一個成功的Promise
Promise.resolve()語法糖
例1:
Promise.resolve("成功")等同于new Promise(function(resolve){resolve("成功")})
例2:
var resolved = Promise.resolve("foo"); resolved.then((str) => console.log(str);//foo )
相當于
var resolved = new Promise((resolve, reject) => { resolve("foo") }); resolved.then((str) => console.log(str);//foo )
Promise.resolve方法有下面三種形式:
Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(theanable);
這三種形式都會產生一個新的Promise。其中:
第一種形式提供了自定義Promise的值的能力,它與Promise.reject(reason)對應。兩者的不同,在于得到的Promise的狀態不同。
第二種形式,提供了創建一個Promise的副本的能力。
第三種形式,是將一個類似Promise的對象轉換成一個真正的Promise對象。它的一個重要作用是將一個其他實現的Promise對象封裝成一個當前實現的Promise對象。例如你正在用bluebird,但是現在有一個Q的Promise,那么你可以通過此方法把Q的Promise變成一個bluebird的Promise。
實際上第二種形式可以歸在第三種形式中。
本節參考文章:ES6中的Promise.resolve()
推薦閱讀:性感的Promise...
7.偽類 / 偽元素 偽類偽類 用于當已有元素處于的某個狀態時,為其添加對應的樣式,這個狀態是根據用戶行為而動態變化的。
當用戶懸停在指定的元素時,我們可以通過 :hover 來描述這個元素的狀態。雖然它和普通的 CSS 類相似,可以為已有的元素添加樣式,但是它只有處于 DOM 樹無法描述的狀態下才能為元素添加樣式,所以將其稱為偽類。
偽元素偽元素 用于創建一些不在文檔樹中的元素,并為其添加樣式。
我們可以通過 :before 來在一個元素前增加一些文本,并為這些文本添加樣式。雖然用戶可以看到這些文本,但是這些文本實際上不在文檔樹中。
本節參考文章:前端面試題-偽類和偽元素、總結偽類與偽元素
8.DOMContentLoaded / loadDOM文檔加載的步驟為:
解析HTML結構。
DOM樹構建完成。//DOMContentLoaded
加載外部腳本和樣式表文件。
解析并執行腳本代碼。
加載圖片等外部文件。
頁面加載完畢。//load
觸發的時機不一樣,先觸發DOMContentLoaded事件,后觸發load事件。
原生js
// 不兼容老的瀏覽器,兼容寫法見[jQuery中ready與load事件](http://www.imooc.com/code/3253),或用jQuery document.addEventListener("DOMContentLoaded", function() { // ...代碼... }, false); window.addEventListener("load", function() { // ...代碼... }, false);
jQuery
// DOMContentLoaded $(document).ready(function() { // ...代碼... }); //load $(document).load(function() { // ...代碼... });
head 中資源的加載
head 中 js 資源加載都會停止后面 DOM 的構建,但是不影響后面資源的下載。
css資源不會阻礙后面 DOM 的構建,但是會阻礙頁面的首次渲染。
body 中資源的加載
body 中 js 資源加載都會停止后面 DOM 的構建,但是不影響后面資源的下載。
css 資源不會阻礙后面 DOM 的構建,但是會阻礙頁面的首次渲染。
DomContentLoaded 事件的觸發
上面只是講了 html 文檔的加載與渲染,并沒有講 DOMContentLoaded 事件的觸發時機。直截了當地結論是,DOMContentLoaded 事件在 html文檔加載完畢,并且 html 所引用的內聯 js、以及外鏈 js 的同步代碼都執行完畢后觸發。
大家可以自己寫一下測試代碼,分別引用內聯 js 和外鏈 js 進行測試。
load 事件的觸發
當頁面 DOM 結構中的 js、css、圖片,以及 js 異步加載的 js、css 、圖片都加載完成之后,才會觸發 load 事件。
注意:
頁面中引用的js 代碼如果有異步加載的 js、css、圖片,是會影響 load 事件觸發的。video、audio、flash 不會影響 load 事件觸發。
推薦閱讀:再談 load 與 DOMContentLoaded
本節參考文章:DOMContentLoaded與load的區別、事件DOMContentLoaded和load的區別
因為瀏覽器生成Dom樹的時候是一行一行讀HTML代碼的,script標簽放在最后面就不會影響前面的頁面的渲染。那么問題來了,既然Dom樹完全生成好后頁面才能渲染出來,瀏覽器又必須讀完全部HTML才能生成完整的Dom樹,script標簽不放在body底部是不是也一樣,因為dom樹的生成需要整個文檔解析完畢。
我們再來看一下chrome在頁面渲染過程中的,綠色標志線是First Paint的時間。納尼,為什么會出現firstpaint,頁面的paint不是在渲染樹生成之后嗎?其實現代瀏覽器為了更好的用戶體驗,渲染引擎將嘗試盡快在屏幕上顯示的內容。它不會等到所有HTML解析之前開始構建和布局渲染樹。部分的內容將被解析并顯示。也就是說瀏覽器能夠渲染不完整的dom樹和cssom,盡快的減少白屏的時間。假如我們將js放在header,js將阻塞解析dom,dom的內容會影響到First Paint,導致First Paint延后。所以說我們會 將js放在后面,以減少First Paint的時間,但是不會減少DOMContentLoaded被觸發的時間。
本節參考文章:DOMContentLoaded與load的區別
10.clientheight / offsetheightclientheight:內容的可視區域,不包含border。clientheight=padding+height-橫向滾動軸高度。
這里寫圖片描述
offsetheight,它包含padding、border、橫向滾動軸高度。
offsetheight=padding+height+border+橫向滾動軸高度
scrollheight,可滾動高度,就是將滾動框拉直,不再滾動的高度,這個很好理解。 It includes the element’s padding, but not its border or margin.
本節參考文章:css clientheight、offsetheight、scrollheight詳解
11.use strict 有什么意義和好處使調試更加容易。那些被忽略或默默失敗了的代碼錯誤,會產生錯誤或拋出異常,因此盡早提醒你代碼中的問題,你才能更快地指引到它們的源代碼。
防止意外的全局變量。如果沒有嚴格模式,將值分配給一個未聲明的變量會自動創建該名稱的全局變量。這是JavaScript中最常見的錯誤之一。在嚴格模式下,這樣做的話會拋出錯誤。
消除 this 強制。如果沒有嚴格模式,引用null或未定義的值到 this 值會自動強制到全局變量。這可能會導致許多令人頭痛的問題和讓人恨不得拔自己頭發的bug。在嚴格模式下,引用 null或未定義的 this 值會拋出錯誤。
不允許重復的屬性名稱或參數值。當檢測到對象中重復命名的屬性,例如:
var object = {foo: "bar", foo: "baz"};)
或檢測到函數中重復命名的參數時,例如:
function foo(val1, val2, val1){})
嚴格模式會拋出錯誤,因此捕捉幾乎可以肯定是代碼中的bug可以避免浪費大量的跟蹤時間。
使 eval() 更安全。在嚴格模式和非嚴格模式下, eval() 的行為方式有所不同。最顯而易見的是,在嚴格模式下,變量和聲明在 eval() 語句內部的函數不會在包含范圍內創建(它們會在非嚴格模式下的包含范圍中被創建,這也是一個常見的問題源)。
在 delete 使用無效時拋出錯誤。 delete 操作符(用于從對象中刪除屬性)不能用在對象不可配置的屬性上。當試圖刪除一個不可配置的屬性時,非嚴格代碼將默默地失敗,而嚴格模式將在這樣的情況下拋出異常。
本節參考文章:經典面試題(4)
12.常見 JavaScript 內存泄漏意外的全局變量
JavaScript 處理未定義變量的方式比較寬松:未定義的變量會在全局對象創建一個新變量。在瀏覽器中,全局對象是 window 。
function foo(arg) { bar = "this is a hidden global variable"; }
真相是: ``` function foo(arg) { window.bar = "this is an explicit global variable"; } ``` 函數 foo 內部忘記使用 var ,意外創建了一個全局變量。此例泄漏了一個簡單的字符串,無傷大雅,但是有更糟的情況。 另一種意外的全局變量可能由 this 創建: ``` function foo() { this.variable = "potential accidental global"; } // Foo 調用自己,this 指向了全局對象(window) // 而不是 undefined foo(); ``` 在 JavaScript 文件頭部加上 "use strict",可以避免此類錯誤發生。啟用嚴格模式解析 JavaScript ,避免意外的全局變量。
被遺忘的計時器或回調函數
在 JavaScript 中使用 setInterval 非常平常。一段常見的代碼:
var someResource = getData(); setInterval(function() { var node = document.getElementById("Node"); if(node) { // 處理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
此例說明了什么:與節點或數據關聯的計時器不再需要,node 對象可以刪除,整個回調函數也不需要了。可是,計時器回調函數仍然沒被回收(計時器停止才會被回收)。同時,someResource 如果存儲了大量的數據,也是無法被回收的。
對于觀察者的例子,一旦它們不再需要(或者關聯的對象變成不可達),明確地移除它們非常重要。老的 IE 6 是無法處理循環引用的。如今,即使沒有明確移除它們,一旦觀察者對象變成不可達,大部分瀏覽器是可以回收觀察者處理函數的。
觀察者代碼示例:
var element = document.getElementById("button"); function onClick(event) { element.innerHTML = "text"; } element.addEventListener("click", onClick);
對象觀察者和循環引用注意事項
老版本的 IE 是無法檢測 DOM 節點與 JavaScript 代碼之間的循環引用,會導致內存泄漏。如今,現代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進的垃圾回收算法,已經可以正確檢測和處理循環引用了。換言之,回收節點內存時,不必非要調用 removeEventListener 了。
脫離 DOM 的引用
有時,保存 DOM 節點內部數據結構很有用。假如你想快速更新表格的幾行內容,把每一行 DOM 存成字典(JSON 鍵值對)或者數組很有意義。此時,同樣的 DOM 元素存在兩個引用:一個在 DOM 樹中,另一個在字典中。將來你決定刪除這些行時,需要把兩個引用都清除。
var elements = { button: document.getElementById("button"), image: document.getElementById("image"), text: document.getElementById("text") }; function doStuff() { image.src = "http://some.url/image"; button.click(); console.log(text.innerHTML); // 更多邏輯 } function removeButton() { // 按鈕是 body 的后代元素 document.body.removeChild(document.getElementById("button")); // 此時,仍舊存在一個全局的 #button 的引用 // elements 字典。button 元素仍舊在內存中,不能被 GC 回收。 }
此外還要考慮 DOM 樹內部或子節點的引用問題。假如你的 JavaScript 代碼中保存了表格某一個
閉包
閉包是 JavaScript 開發的一個關鍵方面:匿名函數可以訪問父級作用域的變量。
避免濫用
本節參考文章:4類 JavaScript 內存泄漏及如何避免
13.引用計數 / 標記清除js垃圾回收有兩種常見的算法:引用計數和標記清除。
引用計數就是跟蹤對象被引用的次數,當一個對象的引用計數為0即沒有其他對象引用它時,說明該對象已經無需訪問了,因此就會回收其所占的內存,這樣,當垃圾回收器下次運行就會釋放引用數為0的對象所占用的內存。
標記清除法是現代瀏覽器常用的一種垃圾收集方式,當變量進入環境(即在一個函數中聲明一個變量)時,就將此變量標記為“進入環境”,進入環境的變量是不能被釋放,因為只有執行流進入相應的環境,就可能會引用它們。而當變量離開環境時,就標記為“離開環境”。
垃圾收集器在運行時會給儲存在內存中的所有變量加上標記,然后會去掉環境中的變量以及被環境中的變量引用的變量的標記,當執行完畢那些沒有存在引用 無法訪問的變量就被加上標記,最后垃圾收集器完成清除工作,釋放掉那些打上標記的變量所占的內存。
function problem() { var A = {}; var B = {}; A.a = B; B.a = A; }
引用計數存在一個弊端就是循環引用問題(上邊)
標記清除不存在循環引用的問題,是因為當函數執行完畢之后,對象A和B就已經離開了所在的作用域,此時兩個變量被標記為“離開環境”,等待被垃圾收集器回收,最后釋放其內存。
分析以下代碼:
function createPerson(name){ var localPerson = new Object(); localPerson.name = name; return localPerson; } var globalPerson = createPerson("Junga"); globalPerson = null;//手動解除全局變量的引用
在這個
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/100981.html
摘要:前端基本功常見概念一點這里前端基本功常見概念二點這里前端基本功常見概念三點這里什么是原型鏈當一個引用類型繼承另一個引用類型的屬性和方法時候就會產生一個原型鏈。函數式編程是聲明式而不是命令式,并且應用程序狀態通過純函數流轉。 前端基本功-常見概念(一) 點這里前端基本功-常見概念(二) 點這里前端基本功-常見概念(三) 點這里 1.什么是原型鏈 當一個引用類型繼承另一個引用類型的屬性和方...
摘要:前端基本功常見概念一點這里前端基本功常見概念二點這里前端基本功常見概念三點這里超文本標記語言,顯示信息,不區分大小寫升級版的,區分大小寫可擴展標記語言被用來傳輸和存儲數據規范采用異步方式加載模塊,模塊的加載不影響它后面語句的運行。 前端基本功-常見概念(一) 點這里前端基本功-常見概念(二) 點這里前端基本功-常見概念(三) 點這里 1.HTML / XML / XHTML html...
摘要:面向對象三大特征繼承性多態性封裝性接口。第五階段封裝一個屬于自己的框架框架封裝基礎事件流冒泡捕獲事件對象事件框架選擇框架。核心模塊和對象全局對象,,,事件驅動,事件發射器加密解密,路徑操作,序列化和反序列化文件流操作服務端與客戶端。 第一階段: HTML+CSS:HTML進階、CSS進階、div+css布局、HTML+css整站開發、 JavaScript基礎:Js基礎教程、js內置對...
摘要:面向對象三大特征繼承性多態性封裝性接口。第五階段封裝一個屬于自己的框架框架封裝基礎事件流冒泡捕獲事件對象事件框架選擇框架。核心模塊和對象全局對象,,,事件驅動,事件發射器加密解密,路徑操作,序列化和反序列化文件流操作服務端與客戶端。 第一階段: HTML+CSS:HTML進階、CSS進階、div+css布局、HTML+css整站開發、 JavaScript基礎:Js基礎教程、js內置對...
閱讀 3033·2021-10-13 09:39
閱讀 1880·2021-09-02 15:15
閱讀 2439·2019-08-30 15:54
閱讀 1803·2019-08-30 14:01
閱讀 2602·2019-08-29 14:13
閱讀 1411·2019-08-29 13:10
閱讀 2731·2019-08-28 18:15
閱讀 3870·2019-08-26 10:20