摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里超文本標(biāo)記語言,顯示信息,不區(qū)分大小寫升級(jí)版的,區(qū)分大小寫可擴(kuò)展標(biāo)記語言被用來傳輸和存儲(chǔ)數(shù)據(jù)規(guī)范采用異步方式加載模塊,模塊的加載不影響它后面語句的運(yùn)行。
前端基本功-常見概念(一) 點(diǎn)這里
前端基本功-常見概念(二) 點(diǎn)這里
前端基本功-常見概念(三) 點(diǎn)這里
html:超文本標(biāo)記語言,顯示信息,不區(qū)分大小寫
xhtml:升級(jí)版的html,區(qū)分大小寫
xml:可擴(kuò)展標(biāo)記語言被用來傳輸和存儲(chǔ)數(shù)據(jù)
2.AMD/CMD/CommonJs/ES6 Module
AMD:AMD規(guī)范采用異步方式加載模塊,模塊的加載不影響它后面語句的運(yùn)行。所有依賴這個(gè)模塊的語句,都定義在一個(gè)回調(diào)函數(shù)中,等到加載完成之后,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。
AMD是requirejs 在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出,提前執(zhí)行,推崇依賴前置。用define()定義模塊,用require()加載模塊,require.config()指定引用路徑等
首先我們需要引入require.js文件和一個(gè)入口文件main.js。main.js中配置require.config()并規(guī)定項(xiàng)目中用到的基礎(chǔ)模塊。
/** 網(wǎng)頁中引入require.js及main.js **/ /** main.js 入口文件/主模塊 **/ // 首先用config()指定各模塊路徑和引用名 require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", //實(shí)際路徑為js/lib/jquery.min.js "underscore": "underscore.min", } }); // 執(zhí)行基本操作 require(["jquery","underscore"],function($,_){ // some code here });
引用模塊的時(shí)候,我們將模塊名放在[]中作為reqiure()的第一參數(shù);如果我們定義的模塊本身也依賴其他模塊,那就需要將它們放在[]中作為define()的第一參數(shù)。
// 定義math.js模塊 define(function () { var basicNum = 0; var add = function (x, y) { return x + y; }; return { add: add, basicNum :basicNum }; }); // 定義一個(gè)依賴underscore.js的模塊 define(["underscore"],function(_){ var classify = function(list){ _.countBy(list,function(num){ return num > 30 ? "old" : "young"; }) }; return { classify :classify }; }) // 引用模塊,將模塊放在[]內(nèi) require(["jquery", "math"],function($, math){ var sum = math.add(10,20); $("#sum").html(sum); });
CMD:seajs 在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出,延遲執(zhí)行,推崇依賴就近
require.js在申明依賴的模塊時(shí)會(huì)在第一之間加載并執(zhí)行模塊內(nèi)的代碼:
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { // 等于在最前面聲明并初始化了要用到的所有模塊 if (false) { // 即便沒用到某個(gè)模塊 b,但 b 還是提前執(zhí)行了 b.foo() } });
CMD是另一種js模塊化方案,它與AMD很類似,不同點(diǎn)在于:AMD 推崇依賴前置、提前執(zhí)行,CMD推崇依賴就近、延遲執(zhí)行。此規(guī)范其實(shí)是在sea.js推廣過程中產(chǎn)生的。
/** CMD寫法 **/ define(function(require, exports, module) { var a = require("./a"); //在需要時(shí)申明 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規(guī)范的主要實(shí)踐者,它有四個(gè)重要的環(huán)境變量為模塊化的實(shí)現(xiàn)提供支持:module、exports、require、global。實(shí)際使用時(shí),用module.exports定義當(dāng)前模塊對(duì)外輸出的接口(不推薦直接用exports),用require加載模塊。
// 定義模塊math.js var basicNum = 0; function add(a, b) { return a + b; } module.exports = { //在這里寫上需要向外暴露的函數(shù)、變量 add: add, basicNum: basicNum } // 引用自定義的模塊時(shí),參數(shù)包含路徑,可省略.js var math = require("./math"); math.add(2, 5); // 引用核心模塊時(shí),不需要帶路徑 var http = require("http"); http.createService(...).listen(3000);
commonJS用同步的方式加載模塊。在服務(wù)端,模塊文件都存在本地磁盤,讀取非常快,所以這樣做不會(huì)有問題。但是在瀏覽器端,限于網(wǎng)絡(luò)原因,更合理的方案是使用異步加載。
ES6 Module:ES6 在語言標(biāo)準(zhǔn)的層面上,實(shí)現(xiàn)了模塊功能,而且實(shí)現(xiàn)得相當(dāng)簡(jiǎn)單,旨在成為瀏覽器和服務(wù)器通用的模塊解決方案。其模塊功能主要由兩個(gè)命令構(gòu)成:export和import。export命令用于規(guī)定模塊的對(duì)外接口,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命令的時(shí)候,用戶需要知道所要加載的變量名或函數(shù)名。其實(shí)ES6還提供了export default命令,為模塊指定默認(rèn)輸出,對(duì)應(yīng)的import語句不需要使用大括號(hào)。這也更趨近于ADM的引用寫法。
/** export default **/ //定義輸出 export default { basicNum, add }; //引入 import math from "./math"; function test(ele) { ele.textContent = math.add(99 + math.basicNum); }
ES6的模塊不是對(duì)象,import命令會(huì)被 JavaScript 引擎靜態(tài)分析,在編譯時(shí)就引入模塊代碼,而不是在代碼運(yùn)行時(shí)加載,所以無法實(shí)現(xiàn)條件加載。也正因?yàn)檫@個(gè),使得靜態(tài)分析成為可能。
ES6 模塊與 CommonJS 模塊的差異
CommonJS 模塊輸出的是一個(gè)值的拷貝,ES6 模塊輸出的是值的引用。
CommonJS 模塊輸出的是值的拷貝,也就是說,一旦輸出一個(gè)值,模塊內(nèi)部的變化就影響不到這個(gè)值。
ES6 模塊的運(yùn)行機(jī)制與 CommonJS 不一樣。JS 引擎對(duì)腳本靜態(tài)分析的時(shí)候,遇到模塊加載命令import,就會(huì)生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。換句話說,ES6 的import有點(diǎn)像 Unix 系統(tǒng)的“符號(hào)連接”,原始值變了,import加載的值也會(huì)跟著變。因此,ES6 模塊是動(dòng)態(tài)引用,并且不會(huì)緩存值,模塊里面的變量綁定其所在的模塊。
CommonJS 模塊是運(yùn)行時(shí)加載,ES6 模塊是編譯時(shí)輸出接口。
運(yùn)行時(shí)加載: CommonJS 模塊就是對(duì)象;即在輸入時(shí)是先加載整個(gè)模塊,生成一個(gè)對(duì)象,然后再從這個(gè)對(duì)象上面讀取方法,這種加載稱為“運(yùn)行時(shí)加載”。
- 編譯時(shí)加載: ES6 模塊不是對(duì)象,而是通過 export 命令顯式指定輸出的代碼,import時(shí)采用靜態(tài)命令的形式。即在import時(shí)可以指定加載某個(gè)輸出值,而不是加載整個(gè)模塊,這種加載稱為“編譯時(shí)加載”。 CommonJS 加載的是一個(gè)對(duì)象(即module.exports屬性),該對(duì)象只有在腳本運(yùn)行完才會(huì)生成。而 ES6 模塊不是對(duì)象,它的對(duì)外接口只是一種靜態(tài)定義,在代碼靜態(tài)解析階段就會(huì)生成。
本節(jié)參考文章:前端模塊化:CommonJS,AMD,CMD,ES6
3.ES5的繼承/ES6的繼承ES5的繼承時(shí)通過prototype或構(gòu)造函數(shù)機(jī)制來實(shí)現(xiàn)。ES5的繼承實(shí)質(zhì)上是先創(chuàng)建子類的實(shí)例對(duì)象,然后再將父類的方法添加到this上(Parent.apply(this))。
ES6的繼承機(jī)制完全不同,實(shí)質(zhì)上是先創(chuàng)建父類的實(shí)例對(duì)象this(所以必須先調(diào)用父類的super()方法),然后再用子類的構(gòu)造函數(shù)修改this。
具體的:ES6通過class關(guān)鍵字定義類,里面有構(gòu)造方法,類之間通過extends關(guān)鍵字實(shí)現(xiàn)繼承。子類必須在constructor方法中調(diào)用super方法,否則新建實(shí)例報(bào)錯(cuò)。因?yàn)樽宇悰]有自己的this對(duì)象,而是繼承了父類的this對(duì)象,然后對(duì)其進(jìn)行加工。如果不調(diào)用super方法,子類得不到this對(duì)象。
ps:super關(guān)鍵字指代父類的實(shí)例,即父類的this對(duì)象。在子類構(gòu)造函數(shù)中,調(diào)用super后,才可使用this關(guān)鍵字,否則報(bào)錯(cuò)。
區(qū)別:(以SubClass,SuperClass,instance為例)
ES5中繼承的實(shí)質(zhì)是:(那種經(jīng)典寄生組合式繼承法)通過prototype或構(gòu)造函數(shù)機(jī)制來實(shí)現(xiàn),先創(chuàng)建子類的實(shí)例對(duì)象,然后再將父類的方法添加到this上(Parent.apply(this))。
先由子類(SubClass)構(gòu)造出實(shí)例對(duì)象this
然后在子類的構(gòu)造函數(shù)中,將父類(SuperClass)的屬性添加到this上,SuperClass.apply(this, arguments)
子類原型(SubClass.prototype)指向父類原型(SuperClass.prototype)
所以instance是子類(SubClass)構(gòu)造出的(所以沒有父類的[[Class]]關(guān)鍵標(biāo)志)
所以,instance有SubClass和SuperClass的所有實(shí)例屬性,以及可以通過原型鏈回溯,獲取SubClass和SuperClass原型上的方法
ES6中繼承的實(shí)質(zhì)是:先創(chuàng)建父類的實(shí)例對(duì)象this(所以必須先調(diào)用父類的super()方法),然后再用子類的構(gòu)造函數(shù)修改this
先由父類(SuperClass)構(gòu)造出實(shí)例對(duì)象this,這也是為什么必須先調(diào)用父類的super()方法(子類沒有自己的this對(duì)象,需先由父類構(gòu)造)
然后在子類的構(gòu)造函數(shù)中,修改this(進(jìn)行加工),譬如讓它指向子類原型(SubClass.prototype),這一步很關(guān)鍵,否則無法找到子類原型(注,子類構(gòu)造中加工這一步的實(shí)際做法是推測(cè)出的,從最終效果來推測(cè))
然后同樣,子類原型(SubClass.prototype)指向父類原型(SuperClass.prototype)
所以instance是父類(SuperClass)構(gòu)造出的(所以有著父類的[[Class]]關(guān)鍵標(biāo)志)
所以,instance有SubClass和SuperClass的所有實(shí)例屬性,以及可以通過原型鏈回溯,獲取SubClass和SuperClass原型上的方法
靜態(tài)方法繼承實(shí)質(zhì)上只需要更改下SubClass.__proto__到SuperClass即可
本節(jié)參考文章:鏈接
4.HTTP request報(bào)文/HTTP response報(bào)文請(qǐng)求報(bào)文 | 響應(yīng)報(bào)文 |
---|---|
請(qǐng)求行 請(qǐng)求頭 空行 請(qǐng)求體 | 狀態(tài)行 響應(yīng)頭 空行 響應(yīng)體 |
HTTP request報(bào)文結(jié)構(gòu)是怎樣的
首行是Request-Line包括:請(qǐng)求方法,請(qǐng)求URI,協(xié)議版本,CRLF
首行之后是若干行請(qǐng)求頭,包括general-header,request-header或者entity-header,每個(gè)一行以CRLF結(jié)束
請(qǐng)求頭和消息實(shí)體之間有一個(gè)CRLF分隔
根據(jù)實(shí)際請(qǐng)求需要可能包含一個(gè)消息實(shí)體 一個(gè)請(qǐng)求報(bào)文例子如下:
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
請(qǐng)求報(bào)文
HTTP response報(bào)文結(jié)構(gòu)是怎樣的
首行是狀態(tài)行包括:HTTP版本,狀態(tài)碼,狀態(tài)描述,后面跟一個(gè)CRLF
首行之后是若干行響應(yīng)頭,包括:通用頭部,響應(yīng)頭部,實(shí)體頭部
響應(yīng)頭部和響應(yīng)實(shí)體之間用一個(gè)CRLF空行分隔
最后是一個(gè)可能的消息實(shí)體 響應(yīng)報(bào)文例子如下:
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}
響應(yīng)報(bào)文
5.面向?qū)ο蟮墓S模式/構(gòu)造函數(shù)工廠模式集中實(shí)例化了對(duì)象,避免實(shí)例化對(duì)象大量重復(fù)問題
//工廠模式 function createObject(a,b){ var obj = new Object(); //集中實(shí)例化 obj.a = a; obj.b = b; obj.c = function () { return this.a + this.b; }; return obj; //返回實(shí)例化對(duì)象 } var box = createObject("abc",10); var box1 = createObject("abcdef",20); alert(box.c()); //返回abc10 alert(box1.c()); //返回abcdef20
//構(gòu)造函數(shù) 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
構(gòu)造函數(shù)相比工廠模式:
沒有集中實(shí)例化
沒有返回對(duì)象實(shí)例
直接將屬性和方法賦值給this
解決了對(duì)象實(shí)例歸屬問題
構(gòu)造函數(shù)編寫規(guī)范:
構(gòu)造函數(shù)也是函數(shù),但是函數(shù)名的第一個(gè)字母大寫
必須使用new運(yùn)算符 + 函數(shù)名(首字母大寫)例如:var box = new Create();
構(gòu)造函數(shù)和普通函數(shù)的區(qū)別:
普通函數(shù),首字母無需大寫
構(gòu)造函數(shù),用普通函數(shù)調(diào)用方式無效
查看歸屬問題,要?jiǎng)?chuàng)建兩個(gè)構(gòu)造函數(shù):
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); //這里要注意:所有的構(gòu)造函數(shù)的對(duì)象都是Object. alert(box instanceof Create); //true alert(box1 instanceof Create); //false alert(box1 instanceof DeskTop); //true6. new Promise / Promise.resolve()
Promise.resolve()可以生成一個(gè)成功的Promise
Promise.resolve()語法糖
例1:
Promise.resolve("成功")等同于new Promise(function(resolve){resolve("成功")})
例2:
var resolved = Promise.resolve("foo"); resolved.then((str) => console.log(str);//foo )
相當(dāng)于
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);
這三種形式都會(huì)產(chǎn)生一個(gè)新的Promise。其中:
第一種形式提供了自定義Promise的值的能力,它與Promise.reject(reason)對(duì)應(yīng)。兩者的不同,在于得到的Promise的狀態(tài)不同。
第二種形式,提供了創(chuàng)建一個(gè)Promise的副本的能力。
第三種形式,是將一個(gè)類似Promise的對(duì)象轉(zhuǎn)換成一個(gè)真正的Promise對(duì)象。它的一個(gè)重要作用是將一個(gè)其他實(shí)現(xiàn)的Promise對(duì)象封裝成一個(gè)當(dāng)前實(shí)現(xiàn)的Promise對(duì)象。例如你正在用bluebird,但是現(xiàn)在有一個(gè)Q的Promise,那么你可以通過此方法把Q的Promise變成一個(gè)bluebird的Promise。
實(shí)際上第二種形式可以歸在第三種形式中。
本節(jié)參考文章:ES6中的Promise.resolve()
推薦閱讀:性感的Promise...
7.偽類 / 偽元素 偽類偽類 用于當(dāng)已有元素處于的某個(gè)狀態(tài)時(shí),為其添加對(duì)應(yīng)的樣式,這個(gè)狀態(tài)是根據(jù)用戶行為而動(dòng)態(tài)變化的。
當(dāng)用戶懸停在指定的元素時(shí),我們可以通過 :hover 來描述這個(gè)元素的狀態(tài)。雖然它和普通的 CSS 類相似,可以為已有的元素添加樣式,但是它只有處于 DOM 樹無法描述的狀態(tài)下才能為元素添加樣式,所以將其稱為偽類。
偽元素偽元素 用于創(chuàng)建一些不在文檔樹中的元素,并為其添加樣式。
我們可以通過 :before 來在一個(gè)元素前增加一些文本,并為這些文本添加樣式。雖然用戶可以看到這些文本,但是這些文本實(shí)際上不在文檔樹中。
本節(jié)參考文章:前端面試題-偽類和偽元素、總結(jié)偽類與偽元素
8.DOMContentLoaded / loadDOM文檔加載的步驟為:
解析HTML結(jié)構(gòu)。
DOM樹構(gòu)建完成。//DOMContentLoaded
加載外部腳本和樣式表文件。
解析并執(zhí)行腳本代碼。
加載圖片等外部文件。
頁面加載完畢。//load
觸發(fā)的時(shí)機(jī)不一樣,先觸發(fā)DOMContentLoaded事件,后觸發(fā)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 資源加載都會(huì)停止后面 DOM 的構(gòu)建,但是不影響后面資源的下載。
css資源不會(huì)阻礙后面 DOM 的構(gòu)建,但是會(huì)阻礙頁面的首次渲染。
body 中資源的加載
body 中 js 資源加載都會(huì)停止后面 DOM 的構(gòu)建,但是不影響后面資源的下載。
css 資源不會(huì)阻礙后面 DOM 的構(gòu)建,但是會(huì)阻礙頁面的首次渲染。
DomContentLoaded 事件的觸發(fā)
上面只是講了 html 文檔的加載與渲染,并沒有講 DOMContentLoaded 事件的觸發(fā)時(shí)機(jī)。直截了當(dāng)?shù)亟Y(jié)論是,DOMContentLoaded 事件在 html文檔加載完畢,并且 html 所引用的內(nèi)聯(lián) js、以及外鏈 js 的同步代碼都執(zhí)行完畢后觸發(fā)。
大家可以自己寫一下測(cè)試代碼,分別引用內(nèi)聯(lián) js 和外鏈 js 進(jìn)行測(cè)試。
load 事件的觸發(fā)
當(dāng)頁面 DOM 結(jié)構(gòu)中的 js、css、圖片,以及 js 異步加載的 js、css 、圖片都加載完成之后,才會(huì)觸發(fā) load 事件。
注意:
頁面中引用的js 代碼如果有異步加載的 js、css、圖片,是會(huì)影響 load 事件觸發(fā)的。video、audio、flash 不會(huì)影響 load 事件觸發(fā)。
推薦閱讀:再談 load 與 DOMContentLoaded
本節(jié)參考文章:DOMContentLoaded與load的區(qū)別、事件DOMContentLoaded和load的區(qū)別
因?yàn)闉g覽器生成Dom樹的時(shí)候是一行一行讀HTML代碼的,script標(biāo)簽放在最后面就不會(huì)影響前面的頁面的渲染。那么問題來了,既然Dom樹完全生成好后頁面才能渲染出來,瀏覽器又必須讀完全部HTML才能生成完整的Dom樹,script標(biāo)簽不放在body底部是不是也一樣,因?yàn)閐om樹的生成需要整個(gè)文檔解析完畢。
我們?cè)賮砜匆幌耤hrome在頁面渲染過程中的,綠色標(biāo)志線是First Paint的時(shí)間。納尼,為什么會(huì)出現(xiàn)firstpaint,頁面的paint不是在渲染樹生成之后嗎?其實(shí)現(xiàn)代瀏覽器為了更好的用戶體驗(yàn),渲染引擎將嘗試盡快在屏幕上顯示的內(nèi)容。它不會(huì)等到所有HTML解析之前開始構(gòu)建和布局渲染樹。部分的內(nèi)容將被解析并顯示。也就是說瀏覽器能夠渲染不完整的dom樹和cssom,盡快的減少白屏的時(shí)間。假如我們將js放在header,js將阻塞解析dom,dom的內(nèi)容會(huì)影響到First Paint,導(dǎo)致First Paint延后。所以說我們會(huì) 將js放在后面,以減少First Paint的時(shí)間,但是不會(huì)減少DOMContentLoaded被觸發(fā)的時(shí)間。
本節(jié)參考文章:DOMContentLoaded與load的區(qū)別
10.clientheight / offsetheightclientheight:內(nèi)容的可視區(qū)域,不包含border。clientheight=padding+height-橫向滾動(dòng)軸高度。
這里寫圖片描述
offsetheight,它包含padding、border、橫向滾動(dòng)軸高度。
offsetheight=padding+height+border+橫向滾動(dòng)軸高度
scrollheight,可滾動(dòng)高度,就是將滾動(dòng)框拉直,不再滾動(dòng)的高度,這個(gè)很好理解。 It includes the element’s padding, but not its border or margin.
本節(jié)參考文章:css clientheight、offsetheight、scrollheight詳解
11.use strict 有什么意義和好處使調(diào)試更加容易。那些被忽略或默默失敗了的代碼錯(cuò)誤,會(huì)產(chǎn)生錯(cuò)誤或拋出異常,因此盡早提醒你代碼中的問題,你才能更快地指引到它們的源代碼。
防止意外的全局變量。如果沒有嚴(yán)格模式,將值分配給一個(gè)未聲明的變量會(huì)自動(dòng)創(chuàng)建該名稱的全局變量。這是JavaScript中最常見的錯(cuò)誤之一。在嚴(yán)格模式下,這樣做的話會(huì)拋出錯(cuò)誤。
消除 this 強(qiáng)制。如果沒有嚴(yán)格模式,引用null或未定義的值到 this 值會(huì)自動(dòng)強(qiáng)制到全局變量。這可能會(huì)導(dǎo)致許多令人頭痛的問題和讓人恨不得拔自己頭發(fā)的bug。在嚴(yán)格模式下,引用 null或未定義的 this 值會(huì)拋出錯(cuò)誤。
不允許重復(fù)的屬性名稱或參數(shù)值。當(dāng)檢測(cè)到對(duì)象中重復(fù)命名的屬性,例如:
var object = {foo: "bar", foo: "baz"};)
或檢測(cè)到函數(shù)中重復(fù)命名的參數(shù)時(shí),例如:
function foo(val1, val2, val1){})
嚴(yán)格模式會(huì)拋出錯(cuò)誤,因此捕捉幾乎可以肯定是代碼中的bug可以避免浪費(fèi)大量的跟蹤時(shí)間。
使 eval() 更安全。在嚴(yán)格模式和非嚴(yán)格模式下, eval() 的行為方式有所不同。最顯而易見的是,在嚴(yán)格模式下,變量和聲明在 eval() 語句內(nèi)部的函數(shù)不會(huì)在包含范圍內(nèi)創(chuàng)建(它們會(huì)在非嚴(yán)格模式下的包含范圍中被創(chuàng)建,這也是一個(gè)常見的問題源)。
在 delete 使用無效時(shí)拋出錯(cuò)誤。 delete 操作符(用于從對(duì)象中刪除屬性)不能用在對(duì)象不可配置的屬性上。當(dāng)試圖刪除一個(gè)不可配置的屬性時(shí),非嚴(yán)格代碼將默默地失敗,而嚴(yán)格模式將在這樣的情況下拋出異常。
本節(jié)參考文章:經(jīng)典面試題(4)
12.常見 JavaScript 內(nèi)存泄漏意外的全局變量
JavaScript 處理未定義變量的方式比較寬松:未定義的變量會(huì)在全局對(duì)象創(chuàng)建一個(gè)新變量。在瀏覽器中,全局對(duì)象是 window 。
function foo(arg) { bar = "this is a hidden global variable"; }
真相是: ``` function foo(arg) { window.bar = "this is an explicit global variable"; } ``` 函數(shù) foo 內(nèi)部忘記使用 var ,意外創(chuàng)建了一個(gè)全局變量。此例泄漏了一個(gè)簡(jiǎn)單的字符串,無傷大雅,但是有更糟的情況。 另一種意外的全局變量可能由 this 創(chuàng)建: ``` function foo() { this.variable = "potential accidental global"; } // Foo 調(diào)用自己,this 指向了全局對(duì)象(window) // 而不是 undefined foo(); ``` 在 JavaScript 文件頭部加上 "use strict",可以避免此類錯(cuò)誤發(fā)生。啟用嚴(yán)格模式解析 JavaScript ,避免意外的全局變量。
被遺忘的計(jì)時(shí)器或回調(diào)函數(shù)
在 JavaScript 中使用 setInterval 非常平常。一段常見的代碼:
var someResource = getData(); setInterval(function() { var node = document.getElementById("Node"); if(node) { // 處理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
此例說明了什么:與節(jié)點(diǎn)或數(shù)據(jù)關(guān)聯(lián)的計(jì)時(shí)器不再需要,node 對(duì)象可以刪除,整個(gè)回調(diào)函數(shù)也不需要了??墒牵?jì)時(shí)器回調(diào)函數(shù)仍然沒被回收(計(jì)時(shí)器停止才會(huì)被回收)。同時(shí),someResource 如果存儲(chǔ)了大量的數(shù)據(jù),也是無法被回收的。
對(duì)于觀察者的例子,一旦它們不再需要(或者關(guān)聯(lián)的對(duì)象變成不可達(dá)),明確地移除它們非常重要。老的 IE 6 是無法處理循環(huán)引用的。如今,即使沒有明確移除它們,一旦觀察者對(duì)象變成不可達(dá),大部分瀏覽器是可以回收觀察者處理函數(shù)的。
觀察者代碼示例:
var element = document.getElementById("button"); function onClick(event) { element.innerHTML = "text"; } element.addEventListener("click", onClick);
對(duì)象觀察者和循環(huán)引用注意事項(xiàng)
老版本的 IE 是無法檢測(cè) DOM 節(jié)點(diǎn)與 JavaScript 代碼之間的循環(huán)引用,會(huì)導(dǎo)致內(nèi)存泄漏。如今,現(xiàn)代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進(jìn)的垃圾回收算法,已經(jīng)可以正確檢測(cè)和處理循環(huán)引用了。換言之,回收節(jié)點(diǎn)內(nèi)存時(shí),不必非要調(diào)用 removeEventListener 了。
脫離 DOM 的引用
有時(shí),保存 DOM 節(jié)點(diǎn)內(nèi)部數(shù)據(jù)結(jié)構(gòu)很有用。假如你想快速更新表格的幾行內(nèi)容,把每一行 DOM 存成字典(JSON 鍵值對(duì))或者數(shù)組很有意義。此時(shí),同樣的 DOM 元素存在兩個(gè)引用:一個(gè)在 DOM 樹中,另一個(gè)在字典中。將來你決定刪除這些行時(shí),需要把兩個(gè)引用都清除。
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")); // 此時(shí),仍舊存在一個(gè)全局的 #button 的引用 // elements 字典。button 元素仍舊在內(nèi)存中,不能被 GC 回收。 }
此外還要考慮 DOM 樹內(nèi)部或子節(jié)點(diǎn)的引用問題。假如你的 JavaScript 代碼中保存了表格某一個(gè)
閉包
閉包是 JavaScript 開發(fā)的一個(gè)關(guān)鍵方面:匿名函數(shù)可以訪問父級(jí)作用域的變量。
避免濫用
本節(jié)參考文章:4類 JavaScript 內(nèi)存泄漏及如何避免
13.引用計(jì)數(shù) / 標(biāo)記清除js垃圾回收有兩種常見的算法:引用計(jì)數(shù)和標(biāo)記清除。
引用計(jì)數(shù)就是跟蹤對(duì)象被引用的次數(shù),當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)為0即沒有其他對(duì)象引用它時(shí),說明該對(duì)象已經(jīng)無需訪問了,因此就會(huì)回收其所占的內(nèi)存,這樣,當(dāng)垃圾回收器下次運(yùn)行就會(huì)釋放引用數(shù)為0的對(duì)象所占用的內(nèi)存。
標(biāo)記清除法是現(xiàn)代瀏覽器常用的一種垃圾收集方式,當(dāng)變量進(jìn)入環(huán)境(即在一個(gè)函數(shù)中聲明一個(gè)變量)時(shí),就將此變量標(biāo)記為“進(jìn)入環(huán)境”,進(jìn)入環(huán)境的變量是不能被釋放,因?yàn)橹挥袌?zhí)行流進(jìn)入相應(yīng)的環(huán)境,就可能會(huì)引用它們。而當(dāng)變量離開環(huán)境時(shí),就標(biāo)記為“離開環(huán)境”。
垃圾收集器在運(yùn)行時(shí)會(huì)給儲(chǔ)存在內(nèi)存中的所有變量加上標(biāo)記,然后會(huì)去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記,當(dāng)執(zhí)行完畢那些沒有存在引用 無法訪問的變量就被加上標(biāo)記,最后垃圾收集器完成清除工作,釋放掉那些打上標(biāo)記的變量所占的內(nèi)存。
function problem() { var A = {}; var B = {}; A.a = B; B.a = A; }
引用計(jì)數(shù)存在一個(gè)弊端就是循環(huán)引用問題(上邊)
標(biāo)記清除不存在循環(huán)引用的問題,是因?yàn)楫?dāng)函數(shù)執(zhí)行完畢之后,對(duì)象A和B就已經(jīng)離開了所在的作用域,此時(shí)兩個(gè)變量被標(biāo)記為“離開環(huán)境”,等待被垃圾收集器回收,最后釋放其內(nèi)存。
分析以下代碼:
function createPerson(name){ var localPerson = new Object(); localPerson.name = name; return localPerson; } var globalPerson = createPerson("Junga"); globalPerson = null;//手動(dòng)解除全局變量的引用
在這個(gè)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100981.html
摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里什么是原型鏈當(dāng)一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法時(shí)候就會(huì)產(chǎn)生一個(gè)原型鏈。函數(shù)式編程是聲明式而不是命令式,并且應(yīng)用程序狀態(tài)通過純函數(shù)流轉(zhuǎn)。 前端基本功-常見概念(一) 點(diǎn)這里前端基本功-常見概念(二) 點(diǎn)這里前端基本功-常見概念(三) 點(diǎn)這里 1.什么是原型鏈 當(dāng)一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方...
摘要:前端基本功常見概念一點(diǎn)這里前端基本功常見概念二點(diǎn)這里前端基本功常見概念三點(diǎn)這里超文本標(biāo)記語言,顯示信息,不區(qū)分大小寫升級(jí)版的,區(qū)分大小寫可擴(kuò)展標(biāo)記語言被用來傳輸和存儲(chǔ)數(shù)據(jù)規(guī)范采用異步方式加載模塊,模塊的加載不影響它后面語句的運(yùn)行。 前端基本功-常見概念(一) 點(diǎn)這里前端基本功-常見概念(二) 點(diǎn)這里前端基本功-常見概念(三) 點(diǎn)這里 1.HTML / XML / XHTML html...
摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個(gè)屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對(duì)象事件框架選擇框架。核心模塊和對(duì)象全局對(duì)象,,,事件驅(qū)動(dòng),事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對(duì)...
摘要:面向?qū)ο笕筇卣骼^承性多態(tài)性封裝性接口。第五階段封裝一個(gè)屬于自己的框架框架封裝基礎(chǔ)事件流冒泡捕獲事件對(duì)象事件框架選擇框架。核心模塊和對(duì)象全局對(duì)象,,,事件驅(qū)動(dòng),事件發(fā)射器加密解密,路徑操作,序列化和反序列化文件流操作服務(wù)端與客戶端。 第一階段: HTML+CSS:HTML進(jìn)階、CSS進(jìn)階、div+css布局、HTML+css整站開發(fā)、 JavaScript基礎(chǔ):Js基礎(chǔ)教程、js內(nèi)置對(duì)...
閱讀 3074·2021-10-13 09:39
閱讀 1904·2021-09-02 15:15
閱讀 2476·2019-08-30 15:54
閱讀 1830·2019-08-30 14:01
閱讀 2630·2019-08-29 14:13
閱讀 1445·2019-08-29 13:10
閱讀 2755·2019-08-28 18:15
閱讀 3959·2019-08-26 10:20