構造函數:function Person(),Person()即為構造函數
原型對象:每一個構造函數都會帶有一個Prototype屬性,該屬性為一個指針,指向了一個對象,即為原型對象。
實例對象:new Person(),new一個構造函數就會產生一個實例,實例對象有一個內部屬性_proto_,指向了原型對象。實例能夠訪問該原型對象的所有屬性和方法。
綜上:三者關系為,每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的指。即,實例通過內部指針可以訪問到原型對象,而原型對象通過constructor指針,又可以找到構造函數。(constructor實質上是實例對象的一個屬性)
實例對象person,繼承來自原型對象的屬性值age和name,內部屬性_proto_指向自己的原型對象(_proto_內部屬性不可訪問)。
原型對象上3個屬性(新增的action,constructor,proto),action為新增的方法(因此實例對象繼承action,能夠執行action函數),contructor即為構造函數,_proto_指向最終的原型對象即為object(在js中,任何對象的頂端都是object)。
構造函數Person實際就是個函數,而其內部屬性prototype指向自己的原型對象,而原型對象中的屬性constructor指向構造函數,并因此不斷的套圈循環 。
1.js是單線程的,且只有一個主線程和一個調用棧(先進后出)
按照先進后出的原則,分別壓入調用棧的為,main.js(即為js代碼塊),b(),a()。所以輸出即為a,b,c。
在調用棧中,前一個函數在執行的時候,下面的函數全部需要等待前一個任務執行完畢,才能執行。當任務很多的時候,效率極其低下。因此js引入任務隊列的概念。
2.js將任務分為同步任務和異步任務
同步任務:在主線程上,前一個任務執行完后,才能執行接下里的任務。
異步任務:分為宏任務和微任務,不進入主線程,而進入任務隊列中。直到任務隊列通知主線程可以執行了,該任務才能執行。
常見宏任務有:script主代碼塊,(setTimeout,setInterval),setImmediate(NodeJs)
常見微任務有:promise,process.nextTick(NodeJs)
注:不同源的任務,會進入不同的任務隊列,上述只有setTimeout和setInterval是同源的。
函數a為宏任務setTimeout,函數c為微任務Promise。
將整個script代碼塊看做是宏任務,宏任務內部執行同步任務,即b();進入b內部
遇到a宏任務push進宏任務隊列中,輸出‘b’,遇到c,Promise分為兩步,一是構造promise對象,此操作為同步,而其回調為異步,因此先輸出‘c’,再將其push進微任務隊列中,再輸出‘finish’。
執行完同步,優先執行微任務隊列,輸出’cc’。直到微任務隊列沒有任務后,
回頭看宏任務隊列只剩下一個setTimeout,執行setTimeout,輸出‘a’。再看微任務隊列中沒有任務,結束循環。
順序為 b,c,finish,cc,a。
上述即為js的事件循環機制,Event loop。
jquery validate。作用域:源程序代碼中定義變量的區域。分為靜態作用域和動態作用域
靜態作用域:也成詞法作用域,函數的作用域在函數定義的時候就決定了。
動態作用域:函數的作用域是在函數被調用的時候決定的。
靜態作用域在定義函數的時候就決定了,因此即便foo()是在bar()中調用,且bar中有同名變量,foo()所輸出的value仍為1,而不是局部同名變量2。
執行上下文棧: js可執行代碼分為全局代碼,函數代碼,eval代碼。而當我們執行到一個函數時,所做的準備工作,即為“執行上下文”,用于管理n個執行上下文的棧即為“執行上下文棧”。
變量對象:每一個執行上下文都會分配一個變量對象,變零對象由變量和函數聲名構成。它保存了當前作用域的所有函數和變量。(只有函數聲名會被加入到變量對象中,而函數表達式并不會)
在函數聲名的方式下,a會被加入到變量對象中,所以在當前作用域下能夠打印出a。
在函數表達式下,b作為變量加入到變量對象中,而_b作為函數表達式則不會加入,
因此能輸出b,而不能輸出_b。
jquery ready方法?關于全局變量的初始化: 當js編譯器開始執行的時候會初始化一個Global Object用于關聯全局的作用域。對于全局環境而言,global object就是變量對象(variable object)。變量對象對于程序而言是不可讀的,只有編譯器才有權訪問變量對象。在瀏覽器端,global object被具象成window對象,也就是說 global object === window === 全局環境的variable object。因此global object對于程序而言也是唯一可讀的variable object。
活動對象: 當函數被調用時,一個活動對象就會被創建并且分配給執行上下文
活動對象由特殊對象argument初始化而成,隨后,他被當做變量對象用于變量初始化。
如上圖代碼,當a被調用時,在a的執行上下文會創建一個活動對象AO,并且被初始化為AO = [arguments],隨后AO又被當做變量對象VO進行變量初始化,此時VO = [arguments].concat([name,age,gender,b]);
執行過程 :
1.進入執行上下文
2.代碼執行。
在進入執行上下文前,正如之前所說的會創建一個活動對象AO,這個AO被初始化為
如果有傳入實參,則有值,反之,則為undefined。
在進入上下文時,此時的AO(活動對象) === VO(變量對象 )。
變量對象會包括:
1.函數的所有形參 (如果是函數上下文):
由名稱和對應值組成的一個變量對象的屬性被創建
沒有實參,屬性值設為 undefined
2.函數聲明:
由名稱和對應值(函數對象(function-object))組成一個變量對象的屬性被創建
如果變量對象已經存在相同名稱的屬性,則完全替換這個屬性
3.變量聲明:
由名稱和對應值(undefined)組成一個變量對象的屬性被創建;
如果變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會干擾已經存在的這類屬性
js跳出foreach循環,舉個例子 :
在執行時的AO = { arguments:{0:1,length:1}a:1,b:undefined,c:reference to function c(){}, d:undefined
}
在執行后的AO = {arguments:{0:1,length:1}a:1,b:3,c:reference to function c(){},d:reference to FunctionExpression "d"
}
解釋:執行foo(1)時,在創建AO時,已經將形參a的值賦值,因此a的值初始化為1。
遇到變量聲明,添加變量名–undefined;
遇到函數聲明,添加函數名–function-object;
(由于d為函數表達式并非函數聲明,因此初始化時將被視為變量聲明,而非函數聲明)。
總結:
1.全局上下文的變量對象初始化是全局對象
2.函數上下文的變量對象初始化只包括 Arguments 對象
3.在進入執行上下文時會給變量對象添加形參、函數聲明、變量聲明等初始的屬性值
4.在代碼執行階段,會再次修改變量對象的屬性值
function foo() {console.log(a);a = 1;
}foo(); // a is not definedfunction bar() {a = 1;console.log(a);
}
bar(); // 1執行foo,輸出a時,a還未被聲明,因此AO中并沒有a這個屬性名,所以報錯 a is not defined。
執行bar,輸出a時,a已經被聲明,因此輸出為1。
將foo修改為
function foo() {console.log(a);var a = 1;
}
foo的輸出為undefined。這是由于var關鍵字具有變量提升,實際上foo為
function foo() {var a;console.log(a);a = 1;
}
因此,在初始化AO時,加入了a這個變量名,對應的值為undefined。而在執行時,按照從上而下的順序,輸出a時
a并沒有被賦值為1,因此輸出為undefined。
同理,將var改為let,const,仍為報錯,即AO中不存在a這個屬性,因為他們不具備變量提升,只有var有。
console.log(foo);function foo(){console.log("foo");
}var foo = 1;
//輸出為函數foo。
執行上下文時,優先處理函數聲明,再處理變量聲明,所以上述代碼為:
var foo;
foo = function(){console.log("foo")
}
foo = 1;
其實就是變量聲明要讓著函數聲明,即使名字一樣,也不能覆蓋。
但同名變量聲明會覆蓋之前的,同名函數聲明也會覆蓋之前的。
在4中,我主要是是對執行上下文的內部做的總結以及代碼測試,所以導致內容過長,理解不清晰,在4.5重新對執行上下文做一個更加明確的定義。
執行上下文包括
js的閉包其實就是:閉包是指那些能夠訪問自由變量的函數
ECMAScript中,閉包指的是:
1.從理論角度:所有的函數。因為它們都在創建的時候就將上層上下文的數據保存起來了。哪怕是簡單的全局變量也是如此,因為函數中訪問全局變量就相當于是在訪問自由變量,這個時候使用最外層的作用域。
2.從實踐角度:以下函數才算是閉包:
I.即使創建它的上下文已經銷毀,它仍然存在(比如,內部函數從父函數中返回)
II.在代碼中引用了自由變量
var scope = "global scope";
function checkscope(){var scope = "local scope";function f(){return scope;}return f;
}var foo = checkscope();
foo();
執行過程:
1.進入全局代碼,創建全局上下文,壓入執行上下文棧
2.全局執行上下文初始化
3.執行checkScope函數,創建checkScope函數執行上下文,checkScope執行上下文被壓入執行上下文棧
4.checkScope上下文初始化,創建變量對象,作用域鏈,this。
5.checkScope函數執行完畢,checkScope執行上下文被執行上下文棧中彈出
6.執行f函數,創建f函數執行上下文,f執行上下文被壓入執行上下文棧
7.f執行上下文初始化,創建變量對象,作用域鏈,this等
8.f函數執行完畢,f函數執行上下文從上下文棧中彈出。當執行f函數時,checkScope函數上下文已經從執行上下文棧中彈出,
理論上f是讀不到checkScope作用域下的scope值。
而f上下文實際上維護著作用域鏈
fContext = {scope:[AO,checkScopeContext.AO,globalContext.VO]
}
這條作用域鏈,會讓f從自己的AO出發,直到globalContext.VO,直到找到scope這個值停止。
如果checkScope.AO中不存在scope,那它就會訪問到globalContext.VO中的scope(即global scope)
即便是checkScopeContext被銷毀,其AO仍將保存在f函數的作用域鏈中。
這實際上就是閉包。
舉個例子
var data = [];for (var i = 0; i < 3; i++) {data[i] = function () {console.log(i);};
}data[0]();
data[1]();
data[2]();
//
data[0]的上下文為:
data[0]Context = {Scope:[AO,globalContext.VO]
}
在這個function內,并沒有定義i,因此它會找到globalContext.VO中的i,
但我們知道只有當執行上下文的時候,function內i的值才會被賦值 ,而此時globalContext.VO.i = 3了,
所以data[0],data[1],data[2]所有的輸出均為3。此時你可認為全局變量i即為自由變量。
那么如何達到我們想要的效果呢?
很簡單,就是在其兩者中間創造一個自由變量
var data = [];for (var i = 0; i < 3; i++) {data[i] = (function (i) {return function(){console.log(i);}})(i);
}
其中后面加了(i)代表了我執行了這個匿名函數,并傳入了初始值i,
也就是匿名函數在for循環內已經在不斷創建上下文,并擁有 i:i鍵值對。
因此他兩者就形成了一個閉包。
data[0]Context = {Scope:[AO,匿名函數的上下文.AO,globalContext.VO]
}
簡單來說,this永遠指向一個對象,且永遠指向其直接調用者。
1.在非嚴格模式下,this默認指向window,而嚴格模式下,this為undefined。
2.作為對象調用,指向直接調用的對象
3.作為函數調用,在非嚴格模式下指向window
4.在new操作符下,指向創造的實例對象
5.可被apply,bind,call改變指向obj。
函數柯里化:柯里化是將一個多參數函數轉換成多個單參數函數,也就是將一個 n 元函數轉換成 n 個一元函數。
偏函數:局部應用則是固定一個函數的一個或者多個參數,也就是將一個 n 元函數轉換成一個 n - x 元函數。
惰性函數:用于優化被多次調用的函數
var t;
function foo() {if (t) return t;t = new Date()return t;
}
上述代碼缺陷:污染全局變量,且每次調用都要進入if語句判斷
1.使用閉包
var foo = (funciton({var t;return fuction(){if (t) return t;t = new Date()return t;}
})()
缺陷:每次仍然需要調用if語句判斷
2.使用惰性函數
var foo = function() {var t = new Date();foo = function() {return t;};return foo();
};
在函數內部重寫函數,即形成了閉包,避免污染了全局,
同時也保證了每次調用不用進行if判斷,因為返回的是foo函數,而非 t。
callee是arguments的屬性,他是正在執行的函數的引用。
caller是調用該函數的函數的引用,也就是this的指向。
Object.defineProperty(obj, prop, descriptor)(ES5):
var obj = {};var value = null;Object.defineProperty(obj,"count",{configurable:true, //描述符可修改?enumerable:true, //對象可枚舉?//數據描述符value:1, //初始值writable:false, //該屬性可被賦值運算符修改?//存取描述符get:function(){console.log('get 了');return value;},set:function(val){console.log('set 了');value = val;}})obj.count = 5;console.log(obj.count);這里以存取描述符為例(因為我們要對其進行監聽)可以看到當我們給count賦值,調用了set方法;輸出count,調用了get方法。由此可見,當我們使用或者修改count屬性的時候,則都可以執行一些自定義操作,這就是監聽。當然,監聽屬性的變化有啥用呢?現假設有一個需求,當我們點擊一次button,lablel的數值就+1。
<div id = "demo">1</div><button id = 'button'>+</button><script>document.getElementById('button').addEventListener('click',function(){var num = + document.getElementById('demo').innerHTMLdocument.getElementById('demo').innerHTML = num + 1;});</script>我們可能很容易想到這樣去做,但是如果改成defineProperty呢<div id = "demo">1</div><button id = 'button'>+</button><script>var obj = {};var value = null;Object.defineProperty(obj,"count",{ get:function(){return value;},set:function(val){value = val;document.getElementById('demo').innerHTML = value;}})document.getElementById('button').addEventListener('click',function(){obj.count +=1;});</script>這就是vue2.0的響應式的原理。
proxy(es6):
var obj = {}var proxy = new Proxy({},{get: function(obj, prop){console.log('get 了')return obj[prop];},set: function(obj, prop, value){console.log('set 了');obj[prop] = value;}});proxy.name = 10;console.log(proxy.name);這里很容易看出區別,proxy實際上是創建了一個實例。而且我監聽不再是一個單一的屬性,而是整個對象,對象內的任意一個屬性發生改變,都可以執行回調。
箭頭函數,其實就是普通函數的簡化版,其實也是匿名函數
Webpack是一種前端資源構建工具。它主要功能就是分析模塊依賴,并將項目文件打包成靜態資源。
基本工作流程:
基本配置
module.export = {mode: 'production', // 設置模式為生產環境entry: './index.ts', // 單個入口resolve: {// 設置支持的擴展名extensions: ['.js', '.ts', '.tsx', '.json']},module: {rules: [// 配置 ts 和 tsx 的加載器{test: /\.(ts|tsx)?$/, // 匹配 .ts 和 .tsx 文件loader: 'ts-loader', // 使用 ts-loader 加載器exclude: /node_modules/ // 排除 node_modules 目錄},// 配置 CSS 樣式文件的加載器{test: /\.css$/, // 匹配 .css 文件loader: 'css-loader', // 使用 css-loader 加載器exclude: /node_modules/ // 排除 node_modules 目錄}],},externals: {"react": "React", // 將 react 標記為擴展依賴"react-dom": "react-dom" // 將 react-dom 標記為擴展依賴},output: {filename: 'index.js', // 定義輸出文件名path: path.resolve(__dirname, "dist") // 定義輸出目錄},// 添加插件plugins: [new CleanWebpackPlugin() // 在編譯前清空輸出目錄]
};
上述代碼為單頁面打包(webpack默認模式),頁面數量和入口文件的數量相同;若要實現多頁面打包,將entry設置為數組即可。
Loader(加載器)
Loader用于轉換模塊的源代碼,通常使用npm管理,需要安裝。由于webpack本身并不具備解析所有資源類型的能力,因此需要各種loader完成這一任務 。
常用的loaders:
Plugin(插件)
plugin是一個擴展器,它豐富了webpack本身,針對是loader結束后,webpack打包的整個過程,它并不直接操作文件,而是基于事件機制工作,會監聽webpack打包過程中的某些節點,執行廣泛的任務
Webpack 與 gulp 的區別
什么是內存泄漏: 不再用到的內存,如果沒有及時釋放,就叫內存泄漏
什么是垃圾: 一般來說沒有被引用的對象就是垃圾。
js常用兩種垃圾回收規則是:
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态