摘要:深入理解原型和閉包王福朋博客園深入理解原型和閉包一切都是對(duì)象原文鏈接本文要點(diǎn)一切引用類型都是對(duì)象,對(duì)象是屬性的集合。每個(gè)對(duì)象都有一個(gè),可稱為隱式原型。另外注意,構(gòu)造函數(shù)的函數(shù)名第一個(gè)字母大寫規(guī)則約定。
深入理解javascript原型和閉包
1. 一切都是對(duì)象王福朋 - 博客園 —— 《 深入理解javascript原型和閉包》
本文要點(diǎn)1原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3977987.html)
一切(引用類型)都是對(duì)象,對(duì)象是屬性的集合。
1. javascript 數(shù)據(jù)類型function show(x) { console.log(typeof x); // undefined console.log(typeof 10); // number console.log(typeof "abc"); // string console.log(typeof true); // boolean console.log(typeof function () {}); //function console.log(typeof [1, "a", true]); //object console.log(typeof { a: 10, b: 20 }); //object console.log(typeof null); //object console.log(typeof new Number(10)); //object } show();
值類型(不是對(duì)象):undefined, number, string, boolean
引用類型(是對(duì)象):函數(shù),數(shù)組,對(duì)象,null, new Number(10)
值類型和引用類型的類型判斷方式:
值類型的類型判斷用 typeof
引用類型的類型判斷使用 instanceof
var fn = function () {}; console.log(fn instanceof Object); // true2. javascript 對(duì)象
定義:若干屬性的集合(只有屬性,沒有方法,方法也是一種屬性)
var obj = { a: 10, b: function (x) { alert(this.a + x) }, c: { name: "tanya", year: 1975 } }
函數(shù)也是一種對(duì)象(可以定義屬性):
var fn = function () { alert(100); }; fn.a = 10; fn.b = function () { alert(123); }; fn.c = { name: "tanya", year: 1975 }
數(shù)組也是一種對(duì)象(本身就存在 length 屬性):
var arr = [1, 2, 3]; console.log(arr.length); // 3 arr.a = "tanya"; arr.b = function () { alert(123); } arr.c = { name: "tanya", year: 1975 } for (var item in arr) { console.log(item) // 0 1 a b c }2. 函數(shù)和對(duì)象的關(guān)系
本文要點(diǎn)2原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3978035.html)
對(duì)象是通過函數(shù)創(chuàng)建的,而函數(shù)又是一種對(duì)象。
對(duì)象是通過函數(shù)創(chuàng)建的function Fn () { this.name = "tanya"; this.age = 1975; } var fn = new Fn(); // 說 fn 是對(duì)象,因?yàn)樗袑傩?console.log(fn.name) // tanya console.log(fn.age) // 1975
通過字面量創(chuàng)建對(duì)象或數(shù)組:
var obj = { a: 10, b: 20 }; var arr = [5, "x", true];
等效于
var obj = new Object(); obj.a = 10; obj.b = 20; var arr = new Array(); arr[0] = 5; arr[1] = "x"; arr[2] = true;
其中 Object 和 Array 是函數(shù):
console.log(typeof Object); // function console.log(typeof Array); // function3. prototype 原型
原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3978131.html)
什么是原型?
函數(shù)都有一個(gè) prototype 屬性,它是一個(gè)對(duì)象,
函數(shù)的實(shí)例對(duì)象的 __proto__ 屬性指向該函數(shù)的 prototype 屬性,
那我們稱該對(duì)象為函數(shù)實(shí)例對(duì)象的原型
本文要點(diǎn)3理解原型概念。
1. prototype 和 constructor 屬性函數(shù)有一個(gè)默認(rèn)屬性 prototype,屬性值是一個(gè)對(duì)象,該對(duì)象默認(rèn)只有一個(gè) constructor 屬性,指向這個(gè)函數(shù)本身。
如上圖:SuperType 是一個(gè)函數(shù),右側(cè)的方框就是它的原型。
Object 原型里面有幾個(gè)其他屬性:
2. 在 prototype 上添加屬性function Fn() {} Fn.prototype.name = "tanya" Fn.prototype.getYear = function () { return 1975; };4. 隱式原型
原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3979290.html)
每個(gè)函數(shù)都有一個(gè) prototype 屬性,即原型。每個(gè)對(duì)象都有一個(gè) __proto__,可稱為隱式原型。
本文要點(diǎn)4理解隱式原型 __proto__。
1. __proto__ 屬性關(guān)于 __proto__ 屬性說明:是一個(gè)隱藏屬性,低版本瀏覽器不支持該屬性。
var obj = {}; console.log(obj.__proto__); console.log(Object.prototype); console.log(obj.__proto__ === Object.prototype);
以上代碼說明:每個(gè)對(duì)象都有一個(gè) __proto__屬性,指向創(chuàng)建該對(duì)象(這里是 obj)的函數(shù)(這里是 Object)的 prototype。
2. Object.prototype 的 __proto__自定義函數(shù)的 prototype:本質(zhì)和 var obj = {} 是一樣的,都是被 Object 創(chuàng)建的,所以它的 __proto__ 指向的就是 Object.prototype。
Object.prototype 是一個(gè)特例——它的 __proto__ 指向的是 null。
3. 函數(shù)的 __proto__函數(shù)也是一種對(duì)象,所以函數(shù)也有 __proto__ 屬性。至于函數(shù)的 __proto__ 是什么,還是要看函數(shù)是被誰創(chuàng)建的。
function fn(x, y) { return x + y; } console.log(fn(10, 20)); // 30 // 等價(jià)于 var fn = new Function("x", "y", "return x + y;"); console.log(fn(5, 6)); // 11
函數(shù)是被 Function 創(chuàng)建的。
上面有說過:對(duì)象的 __proto__ 指向的是創(chuàng)建它的函數(shù)的 prototype,則會(huì)有 Object.__proto__ === Function.prototype。
解釋一下:這里 Object 是一個(gè)函數(shù),被 Function 所創(chuàng)建,函數(shù)也是一種對(duì)象,所以 Object 有 __proto__ 屬性,指向創(chuàng)建它(Object)的函數(shù)(Function)的原型
上圖中,自定義函數(shù)的 Foo.__proto__ 指向 Function.prototype,Object.__proto__ 指向 Function.prototype,還有一個(gè)Function.__proto__ 指向 Function.prototype。
如何理解 Function.__proto__ 指向 Function.prototype?
Function 是一個(gè)函數(shù),函數(shù)是一種對(duì)象,對(duì)象有 __proto__ 屬性。 既然是函數(shù),那么是被 Function 創(chuàng)建的,也就是 Function 是被自身創(chuàng)建的。對(duì)象的 __proto__ 指向創(chuàng)建它的函數(shù)的 prototype,所以 Function 的 __proto__ 指向了自身的 prototype。
5. instanceof本文要點(diǎn)5原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3979290.html)
instanceof 判定規(guī)則。
instanceof 判定語法是 A instanceof B,A是對(duì)象,B一般是一個(gè)函數(shù)。
instanceof 判斷規(guī)則:沿著 A 的 __proto__ 這條線來找,如果能找到與 B.prototype 相等的同一個(gè)引用,即同一個(gè)對(duì)象,就返回 true。如果找到終點(diǎn)(Object.prototype.__proto__)還未重合,則返回 false。
代碼實(shí)現(xiàn):
function instance_of(L, R) { // L 表示左表達(dá)式,R 表示右表達(dá)式 var O = R.prototype; // 取 R 的顯示原型 L = L.__proto__; // 取 L 的隱式原型 while (true) { if (L === null){ return false; } if (O === L) { // 這里重點(diǎn):當(dāng) O 嚴(yán)格等于 L 時(shí),返回 true return true; } L = L.__proto__; } }
根據(jù)上圖及 instanceof 判定規(guī)則理解:
console.log(Object instanceof Function); // true console.log(Function instanceof Object); // true console.log(Function instanceof Function); // true6. “繼承”
本文要點(diǎn)6原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3979985.html)
理解原型鏈概念
知道屬性查找方式
1. 原型鏈javascript 中的繼承是通過原型鏈來體現(xiàn)的。
當(dāng)訪問一個(gè)對(duì)象的屬性時(shí),先在基本屬性(自身屬性)中查找,如果沒有,再沿著 __proto__ 這條鏈向上找,直到 Object.prototype.__proto__,如果還沒找到就返回 undefined,這條在原型上查找的鏈稱為原型鏈。
function Foo() {} var f1 = new Foo(); f1.a = 10; Foo.prototype.a = 100; Foo.prototype.b = 200; console.log(f1.a); // 10 console.log(f1.b); // 2002. hasOwnProperty
通過 hasOwnProperty 方法可以判斷屬性是基本的(自身)還是從原型中找到的。
function Foo() {} var f1 = new Foo(); f1.a = 10; Foo.prototype.a = 100; Foo.prototype.b = 200; for (var item1 in f1) { if (f1.hasOwnProperty(item1)) { // 只打印自身屬性 console.log(item1); // a } } for (var item2 in f1) { console.log(item2); // a b }
那么 hasOwnProperty 有是在哪來的呢?它是在 Objec.prototype 中定義的。
對(duì)象的原型鏈?zhǔn)茄刂?__proto__ 這條線走的,因此在查找 f1.hasOwnProperty 屬性時(shí),就會(huì)順著原型鏈一直查找到 Object.prototype。
每個(gè)函數(shù)都有 call, bind 方法,都有 length, arguments, caller 等屬性。這也是“繼承”的。函數(shù)是由 Function 函數(shù)創(chuàng)建,__proto__ 屬性指向 Function.prototype,因此繼承 Function.prototype 中的方法。
7. 原型靈活性原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3980065.html)
偶不想表^_^。
8. 簡(jiǎn)述【執(zhí)行上下文】上本文要點(diǎn)8原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3986420.html)
理解全局執(zhí)行上下文環(huán)境。
全局執(zhí)行上下文環(huán)境在 javascript 代碼執(zhí)行之前,瀏覽器會(huì)做一些“準(zhǔn)備工作”,其中包括對(duì)變量的聲明,而不是賦值。變量賦值是在賦值語句執(zhí)行的時(shí)候進(jìn)行的。
變量、函數(shù)表達(dá)式——變量聲明,默認(rèn)賦值為undefined;
this——賦值;
函數(shù)聲明——賦值;
這三種數(shù)據(jù)的準(zhǔn)備情況我們稱之為“執(zhí)行上下文”或者“執(zhí)行上下文環(huán)境”。
其實(shí),javascript 在執(zhí)行一個(gè)“代碼段”之前,都會(huì)進(jìn)行這些“準(zhǔn)備工作”來生成執(zhí)行上下文。這個(gè)“代碼段”分為三種情況——全局代碼、函數(shù)體、eval代碼。
為什么“代碼段”分為這三種?
代碼段就是一段文本形式的代碼。首先,全局代碼是一種,本來就是手寫文本到 標(biāo)簽里面的。
其次,eval 代碼接受的也是一段文本形式的代碼。
eval("alert(123)")
最后,函數(shù)體是因?yàn)楹瘮?shù)在創(chuàng)建時(shí),本質(zhì)上是 new Function(...) 得到的,其中需要傳入一個(gè)文本形式的參數(shù)作為函數(shù)體。
function fn(x) { console.log(x + 5); } var fn = new Function("x", "console.log(x + 5)");9. 簡(jiǎn)述【執(zhí)行上下文】下
本文要點(diǎn)9原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3987563.html)
理解執(zhí)行上下文環(huán)境
知道什么是自由變量
上下文的準(zhǔn)備工作及區(qū)別(全局與函數(shù))
1. 函數(shù)體執(zhí)行上下文環(huán)境function fn(x) { console.log(arguments); // [10] console.log(x); // 10 } fn(10);
在函數(shù)體的語句執(zhí)行之前,arguments 變量和函數(shù)參數(shù)都已經(jīng)被賦值。函數(shù)每調(diào)用一次都會(huì)產(chǎn)生一個(gè)新的執(zhí)行上下文環(huán)境,因?yàn)椴煌恼{(diào)用可能會(huì)有不同的參數(shù)。
2. 自由變量自由變量:當(dāng)前作用域內(nèi)使用了外部作用域的變量(使用了不是在當(dāng)前作用域內(nèi)定義的變量)。
函數(shù)在定義的時(shí)候(不是調(diào)用的時(shí)候),就已經(jīng)確定了函數(shù)體內(nèi)部自由變量的作用域。
var a = 10; function fn() { console.log(a); // a 是自由變量,函數(shù)創(chuàng)建時(shí),就確定了 a 要取值的作用域 } function bar(f) { var a = 20; f(); // 打印 10, 而不是 20 } bar(fn);3. 上下文環(huán)境
全局代碼的上下文環(huán)境數(shù)據(jù)內(nèi)容為:
準(zhǔn)備內(nèi)容 | 初始化 |
---|---|
普通變量(包括函數(shù)表達(dá)式),如: var a = 10; | 聲明(默認(rèn)賦值為 undefined) |
函數(shù)聲明, 如: function fn() { } | 賦值 |
this | 賦值 |
如果代碼段是函數(shù)體,那么需要在此(全局準(zhǔn)備內(nèi)容)基礎(chǔ)上附加:
準(zhǔn)備內(nèi)容 | 初始化 |
---|---|
參數(shù) | 賦值 |
arguments | 賦值 |
自由變量的取值作用域 | 賦值 |
通俗執(zhí)行上下文環(huán)境定義:在執(zhí)行代碼之前,把將要用到的所有的變量都事先拿出來,有的直接賦值了,有的先用 undefined 占個(gè)空。
10. this本文要點(diǎn)10原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3988422.html)
新增參考:http://mp.weixin.qq.com/s/xML...
掌握 this 的幾種指向問題。
函數(shù)中 this 取值是在函數(shù)被調(diào)用的時(shí)候確定的,而不是在定義時(shí)。
1. 構(gòu)造函數(shù)所謂構(gòu)造函數(shù)就是用來 new 對(duì)象的函數(shù)。嚴(yán)格來說,所有函數(shù)都可以 new 一個(gè)對(duì)象,但是有些函數(shù)的定義不是為了作為構(gòu)造函數(shù)。另外注意,構(gòu)造函數(shù)的函數(shù)名第一個(gè)字母大寫(規(guī)則約定)。例如:Object, Array, Function 等。
function Foo() { this.name = "tanya"; this.year = 1975; console.log(this); // Foo { name: "tanya", year: 1975 } } var f1 = new Foo(); console.log(f1.name); // tanya console.log(f1.year); // 1975
以上代碼中,如果函數(shù)作為構(gòu)造函數(shù)用,那么其中 this 就代表它即將 new 出來的對(duì)象, 這里 this 表示 f1。
2. 函數(shù)作為對(duì)象的一個(gè)屬性如果函數(shù)作為對(duì)象的一個(gè)屬性時(shí),并且作為對(duì)象的一個(gè)屬性被調(diào)用時(shí),函數(shù)中的 this 指向該對(duì)象。
var obj = { x: 10, fn: function() { console.log(this); // Object { x: 10, fn: function } console.log(this.x); // 10 } } obj.fn();
如果 fn 函數(shù)不是作為 obj 的一個(gè)屬性被調(diào)用,會(huì)是什么結(jié)果?
var obj = { x: 10, fn: function() { console.log(this); // Window {top: Window, ...} console.log(this.x); // undefined } } var fn1 = obj.fn; fn1();
如上代碼,如果 fn 函數(shù)被賦值到了另一個(gè)變量中,并沒有作為 obj 的一個(gè)屬性被調(diào)用,那么 this 的值就是 window,this.x 為 undefined。
3. 函數(shù)用 call 或者 apply 調(diào)用當(dāng)一個(gè)函數(shù)被 call 或 apply 調(diào)用時(shí),this 的值就取傳入的對(duì)象的值。
var obj = { x: 10 }; var fn = function() { console.log(this); // Object {x: 10} console.log(this.x); } fn.call(obj);4. 全局 & 普通函數(shù)調(diào)用(直接調(diào)用)
在全局環(huán)境下,this 永遠(yuǎn)是 window:
console.log(this === window); // true
普通函數(shù)在調(diào)用時(shí),其中 this 也是 window:
var x = 10; var fn = function() { console.log(this); // Window {top: Window ...} console.log(this.x); // 10 } fn();
注意下面的情況:
var obj = { x: 10, fn: function() { function f() { console.log(this); // Window {top: Window ...} console.log(this.x); // 10 } f(); } }; obj.fn();
雖然函數(shù) f 是在 obj.fn 內(nèi)部定義的,但它仍然是一個(gè)普通函數(shù),this 指向 window。
5. bind() 對(duì)直接調(diào)用的影響(新增)Function.prototype.bind() 的作用是將當(dāng)前函數(shù)與指定對(duì)象綁定,并返回一個(gè)新函數(shù),這個(gè)函數(shù)無論以什么樣的方式調(diào)用,其 this 始終指向綁定的對(duì)象。
var obj = {}; function test() { console.log(this === obj); } var testObj = test.bind(obj); test(); // false testObj(); // true6. 箭頭函數(shù)中的 this(新增)
箭頭函數(shù)沒有自己的 this 綁定。箭頭函數(shù)中使用的 this,指的是直接包含它的那個(gè)函數(shù)或函數(shù)表達(dá)式中的 this。
var obj = { test: function () { var arrow = () => { console.log(this === obj); } arrow(); } } obj.test(); // true // 這里 arrow 函數(shù)中的 this 指的是 test 函數(shù)中的 this, // 而 test 函數(shù)中的 this 指的是 obj
11. 執(zhí)行上下文棧另外需要注意的是,箭頭函數(shù)不能用 new 調(diào)用,不能 bind() 到某個(gè)對(duì)象(雖然 bind() 方法調(diào)用沒問題,但是不會(huì)產(chǎn)生預(yù)期效果)。不管在什么情況下使用箭頭函數(shù),它本身是沒有綁定 this 的,它用的是直接外層函數(shù)(即包含它的最近的一層函數(shù)或函數(shù)表達(dá)式)綁定的 this。
本文要點(diǎn)11原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3989357.html)
理解執(zhí)行上下文棧概念
了解壓棧、出棧過程。
1. 執(zhí)行上下文棧概念執(zhí)行全局代碼時(shí),會(huì)產(chǎn)生一個(gè)執(zhí)行上下文環(huán)境,每次調(diào)用函數(shù)都又會(huì)產(chǎn)生執(zhí)行上下文環(huán)境。當(dāng)函數(shù)調(diào)用完成時(shí),這個(gè)上下文環(huán)境以及其中的數(shù)據(jù)都會(huì)被清除,再重新回到全局上下文環(huán)境。處于活動(dòng)狀態(tài)的執(zhí)行上下文環(huán)境只有一個(gè)。
可以把這看成是一個(gè)壓棧出棧的過程,俗稱執(zhí)行上下文棧。
2. 壓棧、出棧過程藍(lán)色背景表示活動(dòng)狀態(tài)
白色背景表示非活動(dòng)狀態(tài)
var a = 10, // 1、進(jìn)入全局上下文環(huán)境 fn, bar = function(x) { var b = 5; fn(x+b);// 用 B 表示 // 3、進(jìn)入 fn 函數(shù)上下文環(huán)境 } fn = function(y) { var c = 5; console.log(y + c); } // 用 A 表示 bar(10); // 2、進(jìn)入 bar 函數(shù)上下文環(huán)境
第一步:在代碼執(zhí)行之前,首先創(chuàng)建全局上下文環(huán)境:
全局上下文環(huán)境(全局代碼執(zhí)行前)
變量 | 賦值 |
---|---|
a | undefined |
fn | undefined |
bar | undefined |
this | window |
然后是代碼執(zhí)行,執(zhí)行到 A 之前,全局上下文環(huán)境中的變量都在執(zhí)行過程中被賦值:
全局上下文環(huán)境變?yōu)?/p>
變量 | 賦值 |
---|---|
a | 10 |
fn | function |
bar | function |
this | window |
第二步:執(zhí)行到 A 之后,調(diào)用 bar 函數(shù)。
跳轉(zhuǎn)到 bar 函數(shù)內(nèi)部,執(zhí)行函數(shù)體語句之前,會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文環(huán)境:
bar 函數(shù)執(zhí)行上下文環(huán)境
變量 | 賦值 |
---|---|
b | undefined |
x | 10 |
arguments | [10]] |
this | window |
并將這個(gè)執(zhí)行上下文環(huán)境壓棧,設(shè)置為活動(dòng)狀態(tài):
第三步:執(zhí)行到 B,又調(diào)用了 fn 函數(shù),在執(zhí)行函數(shù)體語句之前,會(huì)創(chuàng)建 fn 函數(shù)的執(zhí)行上下文環(huán)境,并壓棧,設(shè)置為活動(dòng)狀態(tài):
第四步:待 B 執(zhí)行完畢,即 fn 函數(shù)執(zhí)行完畢后,此次調(diào)用 fn 所生成的上下文環(huán)境出棧,并且被銷毀(已經(jīng)用完了,就要及時(shí)銷毀,釋放內(nèi)存)。
第五步:同理,待 A 執(zhí)行完畢,即 bar 函數(shù)執(zhí)行完畢后,調(diào)用 bar 函數(shù)所生成的上下文環(huán)境出棧,并且被銷毀(已經(jīng)用完了,就要及時(shí)銷毀,釋放內(nèi)存)。
12. 簡(jiǎn)介【作用域】本文要點(diǎn)12原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3991151.html)
參考:https://github.com/mqyqingfen...
理解作用域。
作用域作用域是個(gè)很抽象的概念,類似于一個(gè)“地盤”。
上圖中,全局代碼和 fn 、bar 兩個(gè)函數(shù)都會(huì)形成一個(gè)作用域。
在作用域中存在上下級(jí)關(guān)系,上下級(jí)關(guān)系的確定就看函數(shù)是在哪個(gè)作用域下創(chuàng)建的。例如:fn 作用域下創(chuàng)建了 bar 函數(shù),那么 “fn 作用域” 就是 “bar 作用域” 的上級(jí)。
作用域最大的用處就是隔離變量,不同作用域下同名變量不會(huì)有沖突。例如以上代碼中,三個(gè)作用域下都聲明了“a” 這個(gè)變量,但是他們不會(huì)有沖突。各自作用域下,用各自的“a”。
13. 【作用域】和【上下文環(huán)境】本文要點(diǎn)13原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3991995.html)
理解作用域和上下文環(huán)境。
作用域結(jié)合上下文環(huán)境除了全局作用域外,每個(gè)函數(shù)都會(huì)創(chuàng)建自己的作用域,作用域在函數(shù)定義時(shí)就已經(jīng)確定了,而不是在函數(shù)調(diào)用時(shí)確定的。
下面按照程序執(zhí)行的順序,一步一步加上上下文環(huán)境:
第一步,在加載程序時(shí),就已經(jīng)確定了全局上下文環(huán)境,并隨著程序的執(zhí)行而對(duì)變量賦值:
第二步,程序執(zhí)行到 A ,調(diào)用 fn(10) ,此時(shí)生成此次調(diào)用 fn 函數(shù)時(shí)的上下文環(huán)境,壓棧,并將此上下文環(huán)境設(shè)置為活動(dòng)狀態(tài)。
第三步,執(zhí)行到 B 時(shí),調(diào)用 bar(100) ,生成此次調(diào)用的上下文環(huán)境,壓棧,并設(shè)置為活動(dòng)狀態(tài)。
第四步,執(zhí)行完 B ,bar(100) 調(diào)用完成。則 bar(100) 上下文環(huán)境被銷毀。接著執(zhí)行 C,調(diào)用 bar(200),則又生成 bar(200 )的上下文環(huán)境,壓棧,設(shè)置為活動(dòng)狀態(tài)。
第五步,執(zhí)行完 C ,則 bar(200) 調(diào)用結(jié)束,其上下文環(huán)境被銷毀。此時(shí)會(huì)回到 fn(10) 上下文環(huán)境,變?yōu)榛顒?dòng)狀態(tài)。
第六步,執(zhí)行完 A,fn(10) 執(zhí)行完成之后,fn(10) 上下文環(huán)境被銷毀,全局上下文環(huán)境又回到活動(dòng)狀態(tài)。
最后把以上過程連起來看看:
作用域只是一個(gè)“地盤”,一個(gè)抽象的概念,其中沒有變量,要通過作用域?qū)?yīng)的執(zhí)行上下文環(huán)境來獲取變量的值。同一個(gè)作用域下,不同的調(diào)用會(huì)產(chǎn)生不同的執(zhí)行上下文環(huán)境,繼而產(chǎn)生不同變量的值。所以,作用域中變量的值是在執(zhí)行過程中產(chǎn)生的確定的,而作用域卻是在函數(shù)創(chuàng)建時(shí)就確定了。
所以,如果要找一個(gè)作用域下某個(gè)變量的值,就需要找到這個(gè)作用域?qū)?yīng)的執(zhí)行上下文環(huán)境,再在其中尋找變量的值。
14. 從【自由變量】到【作用域鏈】本文要點(diǎn)14原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3992795.html)
理解作用域鏈。
1. 自由變量什么是自由變量?
在 A 作用域中使用變量 x,卻沒有在 A 作用域中聲明(在其他作用域中聲明的),對(duì)于 A 作用域來說,x就是一個(gè)自由變量。
如下:
var x = 10; function fn() { var b = 20; console.log(x + b); // x 在這里就是一個(gè)自由變量 }
那么,在 fn 函數(shù)中,取自由變量 x 的值時(shí),要到哪個(gè)作用域中去?。俊絼?chuàng)建 fn 函數(shù)的那個(gè)作用域中取。無論 fn 函數(shù)在哪里調(diào)用。
2. 作用域鏈作用域鏈:在當(dāng)前作用域中進(jìn)行查找,如果沒有,就在創(chuàng)建函數(shù)作用域中查找自由變量,如果沒有,就去創(chuàng)建該作用域的函數(shù)所在作用域查找,直到全局作用域?yàn)橹?。這個(gè)在作用域中查找的路線,稱之為作用域鏈。
我們拿文字總結(jié)一下取自由變量時(shí)的這個(gè)“作用域鏈”過程:(假設(shè)a是自由量)
第一步,現(xiàn)在當(dāng)前作用域查找a,如果有則獲取并結(jié)束。如果沒有則繼續(xù);
第二步,如果當(dāng)前作用域是全局作用域,則證明a未定義,結(jié)束;否則繼續(xù);
第三步,(不是全局作用域,那就是函數(shù)作用域)將創(chuàng)建該函數(shù)的作用域作為當(dāng)前作用域;
第四步,跳轉(zhuǎn)到第一步。
示例代碼:
以上代碼中:fn() 返回的是 bar 函數(shù) ,賦值給 x 。執(zhí)行 x(),即執(zhí)行 bar 函數(shù)代碼。取 b 的值時(shí),直接在 fn 作用域取出。取 a 的值時(shí),試圖在 fn 作用域取,但是取不到,只能轉(zhuǎn)向創(chuàng)建 fn 的那個(gè)作用域中去查找,結(jié)果找到了。
15. 閉包本文要點(diǎn)15原文鏈接:http://www.cnblogs.com/wangfupeng1988/p/3992795.html)
參考:https://stackoverflow.com/que...
知道閉包產(chǎn)生的條件及常見用法
理解閉包是什么
1. 閉包產(chǎn)生的條件函數(shù)嵌套
內(nèi)部函數(shù)引用了外部函數(shù)的數(shù)據(jù)(可以是變量或者函數(shù))
換句話說:簡(jiǎn)單地訪問函數(shù)的詞法作用域(靜態(tài)作用域)以外的自由變量會(huì)創(chuàng)建一個(gè)閉包。
function fn () { // 外部函數(shù) var max = 10; function bar (x) { // 內(nèi)部函數(shù) if (x > max) { // 引用了外部函數(shù)變量 max console.log(x) } } } fn()
說明:紅色框中,收起部分為 object: { max: undefined }
那么,閉包到底是什么?
閉包是包含被內(nèi)部函數(shù)引用的在外部函數(shù)中定義的數(shù)據(jù)的對(duì)象(可以是變量或者函數(shù))。簡(jiǎn)單點(diǎn)說是:包含被引用變量(函數(shù))的對(duì)象
2. 常見的閉包將函數(shù)作為另一個(gè)函數(shù)的返回值(函數(shù)不必為了被稱為閉包而返回,看看閉包產(chǎn)生的條件)
將函數(shù)作為實(shí)參傳遞給另一個(gè)函數(shù)調(diào)用
將函數(shù)作為另一個(gè)函數(shù)的返回值
function fn () { var max = 10; function bar () { return max++; } return bar; // 作為另一個(gè)函數(shù)的返回值 } var bar = fn(); console.log(bar()); // 10 console.log(bar()); // 11 console.log(bar()); // 12
從中我們可以看出閉包的作用:
使函數(shù)內(nèi)部的變量 max 在函數(shù) fn 執(zhí)行完后,讓然存活在內(nèi)存中(其他變量已經(jīng)釋放)
讓函數(shù) fn 外部可以讀取到函數(shù)內(nèi)部的數(shù)據(jù) max(變量或者函數(shù))
也可以看出閉包的缺點(diǎn):
內(nèi)存泄漏(Memory Leak)是指程序中己動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費(fèi),導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。
——百度百科
函數(shù) fn 執(zhí)行完之后,被函數(shù) bar 引用的變量沒有釋放,占用內(nèi)存時(shí)間會(huì)變長
可能造成內(nèi)存泄漏(memory leaks)
那對(duì)應(yīng)的解決方案是:
只需要在不使用函數(shù)時(shí),置空就行了 bar = null;
將函數(shù)作為實(shí)參傳遞給另一個(gè)函數(shù)調(diào)用
function fn () { var max = 1; var interval; interval = setInterval(function () { // 將匿名函數(shù)作為 setInterval 函數(shù)的參數(shù)調(diào)用 if (max > 100) clearInterval(interval) console.log(max++); }, 1000) } fn();
注意圖中 Closure (fn),fn 指的是查找引用變量時(shí)的作用域。
3. 閉包作用域代碼一
function fn () { // Closure (fn) { x: 1 } var x = 1; function foo () { // Closure (foo) { y: 2 } var y = 2; console.log(x + y) return function bar () { var z = 3; console.log(x + y + z) } } } var foo = fn(); var bar = foo(); bar();
從上圖中可以看到,閉包所在作用域平行于引用變量(x, y)所在的作用域。
代碼二(繼續(xù)說明閉包所在作用域)
function fn () { var x = 1; function foo () { console.log(x++); } function bar () { console.log(x++); } return { foo: foo, bar: bar } } var o = fn(); var foo = o.foo; var bar = o.bar; foo(); // x: 1 bar(); // x: 2
從代碼輸出可以看到,閉包并不是屬于某一個(gè)內(nèi)部函數(shù),也恰好印證了上面說的。
16. 總結(jié) 文章說明感謝王福朋
內(nèi)容絕大部分來自王福朋 - 博客園 —— 《 深入理解javascript原型和閉包
》,我只是重畫了大部分的圖和對(duì)少量內(nèi)容進(jìn)行了補(bǔ)充(比如:this 章節(jié)的“新增”、閉包部分)
希望在一篇文章中進(jìn)行概括說明這些內(nèi)容(并不是說分開不好),只是這樣讀起來更加順暢,找起來比較方便(離線也可以哦,已上傳到 Github上,歡迎 star、fork);
自己也可以針對(duì)相關(guān)內(nèi)容進(jìn)行擴(kuò)充(不用看到一些額外的相關(guān)知識(shí)就收藏一個(gè)網(wǎng)址^_^,你也可以把這篇文章轉(zhuǎn)到你的賬號(hào)下,進(jìn)行擴(kuò)充,說明來源即可);
文章反饋如有錯(cuò)誤,歡迎指出;
如有疑問,歡迎討論;
文章后續(xù)如果看到相關(guān)的新的知識(shí),會(huì)添加到對(duì)應(yīng)主題下面(以“新增”標(biāo)明);
如果你看到這里沒有提到的相關(guān)內(nèi)容,也可以給我鏈接,我會(huì)進(jìn)行補(bǔ)充,并貼上你的大名;
撒花,待續(xù),期待你們的加入……
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92385.html
摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當(dāng)作缺點(diǎn)提及,但是只要善于運(yùn)用,其實(shí)基于原型的繼承模型比傳統(tǒng)的類繼承還要強(qiáng)大。中文指南基本操作指南二繼續(xù)熟悉的幾對(duì)方法,包括,,。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。 怎樣使用 this 因?yàn)楸救藢儆趥吻岸?,因此文中只看懂?8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...
摘要:也就是說,所有的函數(shù)和構(gòu)造函數(shù)都是由生成,包括本身。如果只考慮構(gòu)造函數(shù)和及其關(guān)聯(lián)的原型對(duì)象,在不解決懸念的情況下,圖形是這樣的可以看到,每一個(gè)構(gòu)造函數(shù)和它關(guān)聯(lián)的原型對(duì)象構(gòu)成一個(gè)環(huán),而且每一個(gè)構(gòu)造函數(shù)的屬性無所指。 前言 JavaScript 是我接觸到的第二門編程語言,第一門是 C 語言。然后才是 C++、Java 還有其它一些什么。所以我對(duì) JavaScript 是非常有感情的,畢...
摘要:情況構(gòu)造函數(shù)所謂構(gòu)造函數(shù)就是用來對(duì)象的函數(shù)。另外注意,構(gòu)造函數(shù)的函數(shù)名第一個(gè)字母大寫規(guī)則約定。閉包但是你只需要知道應(yīng)用的兩種情況即可函數(shù)作為返回值,函數(shù)作為參數(shù)傳遞。如上代碼,函數(shù)作為返回值,賦值給變量。這就是需要理解閉包的核心內(nèi)容。 原文鏈接http://www.cnblogs.com/wangfupeng1988/p/3977924.html 對(duì)象是屬性的集合。 function ...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
摘要:本文是本人閱讀學(xué)習(xí)深入理解原型和閉包時(shí)所作的總結(jié)和筆記,當(dāng)然也引用了很多原文,感興趣的朋友也可以直接去看原文。即這里的稱為隱式原型。注意,構(gòu)造函數(shù)的函數(shù)名第一個(gè)字母大寫規(guī)則約定。但實(shí)際上,上述情況是一種理想的情況。 本文是本人閱讀學(xué)習(xí)深入理解JavaScript原型和閉包時(shí)所作的總結(jié)和筆記,當(dāng)然也引用了很多原文,感興趣的朋友也可以直接去看原文。 1、一切都是對(duì)象 先說結(jié)論,一切引用類型...
閱讀 1227·2023-04-26 02:38
閱讀 1507·2021-11-22 09:34
閱讀 1215·2021-09-26 10:19
閱讀 3209·2019-08-29 17:15
閱讀 3553·2019-08-29 12:27
閱讀 1762·2019-08-26 13:51
閱讀 1896·2019-08-26 13:47
閱讀 1041·2019-08-26 12:20